aiobungie
A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python.
Getting Started
aiobungie provides 3 different client interfaces to get started with, each serve a different purpose.
Client: is probably what you want to get started with first. It provides minimal abstraction for the REST api. Is Pythonic.RESTClient: When you're building a light-weight REST backend. You can use this one. It returnsJSONobjects instead ofdataclasses. This is considered lower-level thatClient.RESTPool: when you're serving a large amount of users and want to spawn a session for each. each instance of this pool returns aRESTClient.
Check either the examples or each of those objects's documentation for more information about the usage.
1# MIT License 2# 3# Copyright (c) 2020 - Present nxtlo 4# 5# Permission is hereby granted, free of charge, to any person obtaining a copy 6# of this software and associated documentation files (the "Software"), to deal 7# in the Software without restriction, including without limitation the rights 8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9# copies of the Software, and to permit persons to whom the Software is 10# furnished to do so, subject to the following conditions: 11# 12# The above copyright notice and this permission notice shall be included in all 13# copies or substantial portions of the Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21# SOFTWARE. 22 23"""A statically typed, asynchronous API wrapper for building clients for Bungie's API in Python. 24 25### Getting Started 26 27aiobungie provides 3 different client interfaces to get started with, each serve a different purpose. 28 29* `Client`: is probably what you want to get started with first. It provides minimal abstraction for the REST api. Is [Pythonic](https://stackoverflow.com/questions/84102/what-is-idiomatic-code#84270). 30* `RESTClient`: When you're building a light-weight REST backend. You can use this one. It returns `JSON` objects instead of `dataclasses`. 31This is considered lower-level that `Client`. 32* `RESTPool`: when you're serving a large amount of users and want to spawn a session for each. each instance of this pool returns a `RESTClient`. 33 34Check either the examples or each of those objects's documentation for more information about the usage. 35""" 36 37from __future__ import annotations 38 39from aiobungie import builders 40from aiobungie import crates 41from aiobungie import interfaces 42from aiobungie import traits 43from aiobungie import typedefs 44from aiobungie import url 45from aiobungie.client import Client 46from aiobungie.error import * 47from aiobungie.internal import iterators 48from aiobungie.internal.assets import Image 49from aiobungie.internal.enums import * 50from aiobungie.internal.factory import EmptyFactory 51from aiobungie.internal.factory import Factory 52from aiobungie.internal.iterators import * 53from aiobungie.rest import * 54 55# Activity enums 56from .crates.activity import Difficulty 57 58# Components enums 59from .crates.components import ComponentFields 60from .crates.components import ComponentPrivacy 61 62# Entity enums 63from .crates.entity import GatingScope 64from .crates.entity import ObjectiveUIStyle 65from .crates.entity import ValueUIStyle 66 67# Fireteam enums. 68from .crates.fireteams import FireteamActivity 69from .crates.fireteams import FireteamDate 70from .crates.fireteams import FireteamLanguage 71from .crates.fireteams import FireteamPlatform 72 73# Records enums 74from .crates.records import RecordState 75from .metadata import __about__ 76from .metadata import __author__ 77from .metadata import __docs__ 78from .metadata import __email__ 79from .metadata import __license__ 80from .metadata import __url__ 81from .metadata import __version__ 82 83__all__ = [mod for mod in dir() if not mod.startswith("_")] # type: ignore
70@attrs.define(auto_exc=True) 71class AiobungieError(RuntimeError): 72 """Base class that all other exceptions inherit from."""
Base class that all other exceptions inherit from.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
635@typing.final 636class AmmoType(int, Enum): 637 """AN enum for Detyiny 2 ammo types.""" 638 639 NONE = 0 640 PRIMARY = 1 641 SPECIAL = 2 642 HEAVY = 3
AN enum for Detyiny 2 ammo types.
163@attrs.define(auto_exc=True) 164class BadRequest(HTTPError): 165 """An exception raised when requesting a resource with the provided data is wrong.""" 166 167 url: typedefs.StrOrURL | None 168 """The URL/endpoint caused this error.""" 169 170 body: typing.Any 171 """The response body.""" 172 173 headers: multidict.CIMultiDictProxy[str] 174 """The response headers.""" 175 176 http_status: http.HTTPStatus = attrs.field( 177 default=http.HTTPStatus.BAD_REQUEST, init=False 178 )
An exception raised when requesting a resource with the provided data is wrong.
2def __init__(self, message, url, body, headers): 3 self.message = message 4 self.url = url 5 self.body = body 6 self.headers = headers 7 self.http_status = attr_dict['http_status'].default 8 BaseException.__init__(self, self.message,self.url,self.body,self.headers)
Method generated by attrs for class BadRequest.
690@typing.final 691class ClanMemberType(int, Enum): 692 """An enum for bungie clan member types.""" 693 694 NONE = 0 695 BEGINNER = 1 696 MEMBER = 2 697 ADMIN = 3 698 ACTING_FOUNDER = 4 699 FOUNDER = 5
An enum for bungie clan member types.
466@typing.final 467class Class(int, Enum): 468 """An Enum for Destiny character classes.""" 469 470 TITAN = 0 471 HUNTER = 1 472 WARLOCK = 2 473 UNKNOWN = 3
An Enum for Destiny character classes.
55class Client(traits.ClientApp): 56 """Standard Bungie API client implementation. 57 58 This client deserialize the REST JSON responses using `aiobungie.Factory` 59 and returns `aiobungie.crates` Python object implementations of the responses. 60 61 Example 62 ------- 63 ```py 64 import aiobungie 65 66 client = aiobungie.Client('...') 67 68 async def main(): 69 async with client.rest: 70 user = await client.fetch_current_user_memberships('...') 71 print(user) 72 ``` 73 74 Parameters 75 ----------- 76 token: `str` 77 Your Bungie's API key or Token from the developer's portal. 78 79 Other Parameters 80 ---------------- 81 max_retries : `int` 82 The max retries number to retry if the request hit a `5xx` status code. 83 client_secret : `str | None` 84 An optional application client secret, 85 This is only needed if you're fetching OAuth2 tokens with this client. 86 client_id : `int | None` 87 An optional application client id, 88 This is only needed if you're fetching OAuth2 tokens with this client. 89 debug: `"TRACE" | bool | int` 90 The level of logging to enable. 91 """ 92 93 __slots__ = ("_rest", "_factory") 94 95 def __init__( 96 self, 97 token: str, 98 /, 99 *, 100 client_secret: str | None = None, 101 client_id: int | None = None, 102 max_retries: int = 4, 103 debug: typing.Literal["TRACE"] | bool | int = False, 104 ) -> None: 105 self._rest = rest_.RESTClient( 106 token, 107 client_secret=client_secret, 108 client_id=client_id, 109 max_retries=max_retries, 110 debug=debug, 111 ) 112 113 self._factory = factory_.Factory(self) 114 115 @property 116 def factory(self) -> factory_.Factory: 117 return self._factory 118 119 @property 120 def rest(self) -> interfaces.RESTInterface: 121 return self._rest 122 123 @property 124 def request(self) -> Client: 125 return self 126 127 @property 128 def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: 129 return self._rest.metadata 130 131 def run(self, fn: collections.Awaitable[typing.Any], debug: bool = False) -> None: 132 loop = helpers.get_or_make_loop() 133 134 try: 135 if not loop.is_running(): 136 loop.set_debug(debug) 137 loop.run_until_complete(fn) 138 139 except Exception as exc: 140 raise RuntimeError(f"Failed to run {fn!s}") from exc 141 142 # * User methods. 143 144 async def fetch_current_user_memberships(self, access_token: str, /) -> user.User: 145 """Fetch and return a user object of the bungie net user associated with account. 146 147 .. warning:: 148 This method requires OAuth2 scope and a Bearer access token. 149 150 Parameters 151 ---------- 152 access_token : `str` 153 A valid Bearer access token for the authorization. 154 155 Returns 156 ------- 157 `aiobungie.crates.user.User` 158 A user object includes the Destiny memberships and Bungie.net user. 159 """ 160 resp = await self.rest.fetch_current_user_memberships(access_token) 161 162 return self.factory.deserialize_user(resp) 163 164 async def fetch_bungie_user(self, id: int, /) -> user.BungieUser: 165 """Fetch a Bungie user by their BungieNet id. 166 167 .. note:: 168 This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id` 169 for other memberships. 170 171 Parameters 172 ---------- 173 id: `int` 174 The user id. 175 176 Returns 177 ------- 178 `aiobungie.crates.user.BungieUser` 179 A Bungie user. 180 181 Raises 182 ------ 183 `aiobungie.error.NotFound` 184 The user was not found. 185 """ 186 payload = await self.rest.fetch_bungie_user(id) 187 188 return self.factory.deserialize_bungie_user(payload) 189 190 async def search_users( 191 self, name: str, / 192 ) -> iterators.Iterator[user.SearchableDestinyUser]: 193 """Search for players and return all players that matches the same name. 194 195 Parameters 196 ---------- 197 name : `str` 198 The user name. 199 200 Returns 201 ------- 202 `aiobungie.Iterator[aiobungie.crates.SearchableDestinyUser]` 203 A sequence of the found users with this name. 204 """ 205 payload = await self.rest.search_users(name) 206 207 return iterators.Iterator( 208 [ 209 self.factory.deserialize_searched_user(user) 210 for user in payload["searchResults"] 211 ] 212 ) 213 214 async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]: 215 """Fetch all available user themes. 216 217 Returns 218 ------- 219 `collections.Sequence[aiobungie.crates.user.UserThemes]` 220 A sequence of user themes. 221 """ 222 data = await self.rest.fetch_user_themes() 223 224 return self.factory.deserialize_user_themes(data) 225 226 async def fetch_hard_types( 227 self, 228 credential: int, 229 type: enums.CredentialType | int = enums.CredentialType.STEAMID, 230 /, 231 ) -> user.HardLinkedMembership: 232 """Gets any hard linked membership given a credential. 233 Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now. 234 Cross Save aware. 235 236 Parameters 237 ---------- 238 credential: `int` 239 A valid SteamID64 240 type: `aiobungie.CredentialType` 241 The credential type. This must not be changed 242 Since its only credential that works "currently" 243 244 Returns 245 ------- 246 `aiobungie.crates.user.HardLinkedMembership` 247 Information about the hard linked data. 248 """ 249 250 payload = await self.rest.fetch_hardlinked_credentials(credential, type) 251 252 return user.HardLinkedMembership( 253 id=int(payload["membershipId"]), 254 type=enums.MembershipType(payload["membershipType"]), 255 cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]), 256 ) 257 258 async def fetch_membership_from_id( 259 self, 260 id: int, 261 /, 262 type: enums.MembershipType | int = enums.MembershipType.NONE, 263 ) -> user.User: 264 """Fetch Bungie user's memberships from their id. 265 266 Notes 267 ----- 268 * This returns both BungieNet membership and a sequence of the player's DestinyMemberships 269 Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, 270 see `aiobungie.crates.user.DestinyMembership` for more details. 271 * If you only want the bungie user. Consider using `Client.fetch_user` method. 272 273 Parameters 274 ---------- 275 id : `int` 276 The user's id. 277 type : `aiobungie.MembershipType` 278 The user's membership type. 279 280 Returns 281 ------- 282 `aiobungie.crates.User` 283 A Bungie user with their membership types. 284 285 Raises 286 ------ 287 aiobungie.NotFound 288 The requested user was not found. 289 """ 290 payload = await self.rest.fetch_membership_from_id(id, type) 291 292 return self.factory.deserialize_user(payload) 293 294 async def fetch_user_credentials( 295 self, access_token: str, membership_id: int, / 296 ) -> collections.Sequence[user.UserCredentials]: 297 """Fetch an array of credential types attached to the requested account. 298 299 .. note:: 300 This method require OAuth2 Bearer access token. 301 302 Parameters 303 ---------- 304 access_token : `str` 305 The bearer access token associated with the bungie account. 306 membership_id : `int` 307 The id of the membership to return. 308 309 Returns 310 ------- 311 `collections.Sequence[aiobungie.crates.UserCredentials]` 312 A sequence of the attached user credentials. 313 314 Raises 315 ------ 316 `aiobungie.Unauthorized` 317 The access token was wrong or no access token passed. 318 """ 319 resp = await self.rest.fetch_user_credentials(access_token, membership_id) 320 321 return self.factory.deserialize_user_credentials(resp) 322 323 # * Destiny 2. 324 325 async def fetch_profile( 326 self, 327 member_id: int, 328 type: enums.MembershipType | int, 329 components: collections.Sequence[enums.ComponentType], 330 auth: str | None = None, 331 ) -> components.Component: 332 """ 333 Fetch a bungie profile passing components to the request. 334 335 Parameters 336 ---------- 337 member_id: `int` 338 The member's id. 339 type: `aiobungie.MembershipType` 340 A valid membership type. 341 components : `collections.Sequence[aiobungie.ComponentType]` 342 List of profile components to collect and return. 343 344 Other Parameters 345 ---------------- 346 auth : `str | None` 347 A Bearer access_token to make the request with. 348 This is optional and limited to components that only requires an Authorization token. 349 350 Returns 351 -------- 352 `aiobungie.crates.Component` 353 A Destiny 2 player profile with its components. 354 Only passed components will be available if they exists. Otherwise they will be `None` 355 356 Raises 357 ------ 358 `aiobungie.MembershipTypeError` 359 The provided membership type was invalid. 360 """ 361 data = await self.rest.fetch_profile(member_id, type, components, auth) 362 return self.factory.deserialize_components(data) 363 364 async def fetch_linked_profiles( 365 self, 366 member_id: int, 367 member_type: enums.MembershipType | int, 368 /, 369 *, 370 all: bool = False, 371 ) -> profile.LinkedProfile: 372 """Returns a summary information about all profiles linked to the requested member. 373 374 The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships. 375 376 .. note:: 377 It will only return linked accounts whose linkages you are allowed to view. 378 379 Parameters 380 ---------- 381 member_id : `int` 382 The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID. 383 member_type : `aiobungie.MembershipType` 384 The type for the membership whose linked Destiny account you want to return. 385 386 Other Parameters 387 ---------------- 388 all : `bool` 389 If provided and set to `True`, All memberships regardless 390 of whether they're obscured by overrides will be returned, 391 392 If provided and set to `False`, Only available memberships will be returned. 393 The default for this is `False`. 394 395 Returns 396 ------- 397 `aiobungie.crates.profile.LinkedProfile` 398 A linked profile object. 399 """ 400 resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all) 401 402 return self.factory.deserialize_linked_profiles(resp) 403 404 async def fetch_membership( 405 self, 406 name: str, 407 code: int, 408 /, 409 type: enums.MembershipType | int = enums.MembershipType.ALL, 410 ) -> collections.Sequence[user.DestinyMembership]: 411 """Fetch a Destiny 2 player's memberships. 412 413 Parameters 414 ----------- 415 name: `str` 416 The unique Bungie player name. 417 code : `int` 418 The unique Bungie display name code. 419 type: `aiobungie.internal.enums.MembershipType` 420 The player's membership type, e,g. XBOX, STEAM, PSN 421 422 Returns 423 -------- 424 `collections.Sequence[aiobungie.crates.DestinyMembership]` 425 A sequence of the found Destiny 2 player memberships. 426 An empty sequence will be returned if no one found. 427 428 Raises 429 ------ 430 `aiobungie.MembershipTypeError` 431 The provided membership type was invalid. 432 """ 433 resp = await self.rest.fetch_membership(name, code, type) 434 435 return self.factory.deserialize_destiny_memberships(resp) 436 437 async def fetch_character( 438 self, 439 member_id: int, 440 membership_type: enums.MembershipType | int, 441 character_id: int, 442 components: collections.Sequence[enums.ComponentType], 443 auth: str | None = None, 444 ) -> components.CharacterComponent: 445 """Fetch a Destiny 2 character. 446 447 Parameters 448 ---------- 449 member_id: `int` 450 A valid bungie member id. 451 character_id: `int` 452 The Destiny character id to retrieve. 453 membership_type: `aiobungie.internal.enums.MembershipType` 454 The member's membership type. 455 components: `collections.Sequence[aiobungie.ComponentType]` 456 Multiple arguments of character components to collect and return. 457 458 Other Parameters 459 ---------------- 460 auth : `str | None` 461 A Bearer access_token to make the request with. 462 This is optional and limited to components that only requires an Authorization token. 463 464 Returns 465 ------- 466 `aiobungie.crates.CharacterComponent` 467 A Bungie character component. 468 469 `aiobungie.MembershipTypeError` 470 The provided membership type was invalid. 471 """ 472 resp = await self.rest.fetch_character( 473 member_id, membership_type, character_id, components, auth 474 ) 475 476 return self.factory.deserialize_character_component(resp) 477 478 async def fetch_unique_weapon_history( 479 self, 480 membership_id: int, 481 character_id: int, 482 membership_type: enums.MembershipType | int, 483 ) -> collections.Sequence[activity.ExtendedWeaponValues]: 484 """Fetch details about unique weapon usage for a character. Includes all exotics. 485 486 Parameters 487 ---------- 488 membership_id : `int` 489 The Destiny user membership id. 490 character_id : `int` 491 The character id to retrieve. 492 membership_type : `aiobungie.aiobungie.MembershipType | int` 493 The Destiny user's membership type. 494 495 Returns 496 ------- 497 `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]` 498 A sequence of the weapon's extended values. 499 """ 500 resp = await self._rest.fetch_unique_weapon_history( 501 membership_id, character_id, membership_type 502 ) 503 504 return tuple( 505 self._factory.deserialize_extended_weapon_values(weapon) 506 for weapon in resp["weapons"] 507 ) 508 509 # * Destiny 2 Activities. 510 511 async def fetch_activities( 512 self, 513 member_id: int, 514 character_id: int, 515 mode: enums.GameMode | int, 516 *, 517 membership_type: enums.MembershipType | int = enums.MembershipType.ALL, 518 page: int = 0, 519 limit: int = 250, 520 ) -> iterators.Iterator[activity.Activity]: 521 """Fetch a Destiny 2 activity for the specified character id. 522 523 Parameters 524 ---------- 525 member_id: `int` 526 The user id that starts with `4611`. 527 character_id: `int` 528 The id of the character to retrieve the activities for. 529 mode: `aiobungie.aiobungie.internal.enums.GameMode | int` 530 This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc. 531 532 Other Parameters 533 ---------------- 534 membership_type: `aiobungie.internal.enums.MembershipType` 535 The Member ship type, if nothing was passed than it will return all. 536 page: int 537 The page number. Default is `0` 538 limit: int 539 Limit the returned result. Default is `250`. 540 541 Returns 542 ------- 543 `aiobungie.Iterator[aiobungie.crates.Activity]` 544 An iterator of the player's activities. 545 546 Raises 547 ------ 548 `aiobungie.MembershipTypeError` 549 The provided membership type was invalid. 550 """ 551 resp = await self.rest.fetch_activities( 552 member_id, 553 character_id, 554 mode, 555 membership_type=membership_type, 556 page=page, 557 limit=limit, 558 ) 559 560 return self.factory.deserialize_activities(resp) 561 562 async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity: 563 """Fetch a post activity details. 564 565 Parameters 566 ---------- 567 instance_id: `int` 568 The activity instance id. 569 570 Returns 571 ------- 572 `aiobungie.crates.PostActivity` 573 A post activity object. 574 """ 575 resp = await self.rest.fetch_post_activity(instance_id) 576 577 return self.factory.deserialize_post_activity(resp) 578 579 async def fetch_aggregated_activity_stats( 580 self, 581 character_id: int, 582 membership_id: int, 583 membership_type: enums.MembershipType | int, 584 ) -> iterators.Iterator[activity.AggregatedActivity]: 585 """Fetch aggregated activity stats for a character. 586 587 Parameters 588 ---------- 589 character_id: `int` 590 The id of the character to retrieve the activities for. 591 membership_id: `int` 592 The id of the user that started with `4611`. 593 membership_type: `aiobungie.internal.enums.MembershipType` 594 The Member ship type. 595 596 Returns 597 ------- 598 `aiobungie.Iterator[aiobungie.crates.AggregatedActivity]` 599 An iterator of the player's activities. 600 601 Raises 602 ------ 603 `aiobungie.MembershipTypeError` 604 The provided membership type was invalid. 605 """ 606 resp = await self.rest.fetch_aggregated_activity_stats( 607 character_id, membership_id, membership_type 608 ) 609 610 return self.factory.deserialize_aggregated_activities(resp) 611 612 # * Destiny 2 Clans or GroupsV2. 613 614 async def fetch_clan_from_id( 615 self, 616 id: int, 617 /, 618 access_token: str | None = None, 619 ) -> clans.Clan: 620 """Fetch a Bungie Clan by its id. 621 622 Parameters 623 ----------- 624 id: `int` 625 The clan id. 626 627 Returns 628 -------- 629 `aiobungie.crates.Clan` 630 An Bungie clan. 631 632 Raises 633 ------ 634 `aiobungie.NotFound` 635 The clan was not found. 636 """ 637 resp = await self.rest.fetch_clan_from_id(id, access_token) 638 639 return self.factory.deserialize_clan(resp) 640 641 async def fetch_clan( 642 self, 643 name: str, 644 /, 645 access_token: str | None = None, 646 *, 647 type: enums.GroupType | int = enums.GroupType.CLAN, 648 ) -> clans.Clan: 649 """Fetch a Clan by its name. 650 This method will return the first clan found with given name. 651 652 Parameters 653 ---------- 654 name: `str` 655 The clan name 656 657 Other Parameters 658 ---------------- 659 access_token : `str | None` 660 An optional access token to make the request with. 661 662 If the token was bound to a member of the clan, 663 This field `aiobungie.crates.Clan.current_user_membership` will be available 664 and will return the membership of the user who made this request. 665 type : `aiobungie.GroupType` 666 The group type, Default is aiobungie.GroupType.CLAN. 667 668 Returns 669 ------- 670 `aiobungie.crates.Clan` 671 A Bungie clan. 672 673 Raises 674 ------ 675 `aiobungie.NotFound` 676 The clan was not found. 677 """ 678 resp = await self.rest.fetch_clan(name, access_token, type=type) 679 680 return self.factory.deserialize_clan(resp) 681 682 async def fetch_clan_conversations( 683 self, clan_id: int, / 684 ) -> collections.Sequence[clans.ClanConversation]: 685 """Fetch the conversations/chat channels of the given clan id. 686 687 Parameters 688 ---------- 689 clan_id : `int` 690 The clan id. 691 692 Returns 693 `collections.Sequence[aiobungie.crates.ClanConversation]` 694 A sequence of the clan chat channels. 695 """ 696 resp = await self.rest.fetch_clan_conversations(clan_id) 697 698 return self.factory.deserialize_clan_conversations(resp) 699 700 async def fetch_clan_admins( 701 self, clan_id: int, / 702 ) -> iterators.Iterator[clans.ClanMember]: 703 """Fetch the clan founder and admins. 704 705 Parameters 706 ---------- 707 clan_id : `int` 708 The clan id. 709 710 Returns 711 ------- 712 `aiobungie.Iterator[aiobungie.crates.ClanMember]` 713 An iterator over the found clan admins and founder. 714 715 Raises 716 ------ 717 `aiobungie.NotFound` 718 The requested clan was not found. 719 """ 720 resp = await self.rest.fetch_clan_admins(clan_id) 721 722 return self.factory.deserialize_clan_members(resp) 723 724 async def fetch_groups_for_member( 725 self, 726 member_id: int, 727 member_type: enums.MembershipType | int, 728 /, 729 *, 730 filter: int = 0, 731 group_type: enums.GroupType = enums.GroupType.CLAN, 732 ) -> collections.Sequence[clans.GroupMember]: 733 """Fetch information about the groups that a given member has joined. 734 735 Parameters 736 ---------- 737 member_id : `int` 738 The member's id 739 member_type : `aiobungie.MembershipType` 740 The member's membership type. 741 742 Other Parameters 743 ---------------- 744 filter : `int` 745 Filter apply to list of joined groups. This Default to `0` 746 group_type : `aiobungie.GroupType` 747 The group's type. 748 This is always set to `aiobungie.GroupType.CLAN` and should not be changed. 749 750 Returns 751 ------- 752 `collections.Sequence[aiobungie.crates.GroupMember]` 753 A sequence of joined groups for the fetched member. 754 """ 755 resp = await self.rest.fetch_groups_for_member( 756 member_id, member_type, filter=filter, group_type=group_type 757 ) 758 759 return tuple( 760 self.factory.deserialize_group_member(group) for group in resp["results"] 761 ) 762 763 async def fetch_potential_groups_for_member( 764 self, 765 member_id: int, 766 member_type: enums.MembershipType | int, 767 /, 768 *, 769 filter: int = 0, 770 group_type: enums.GroupType | int = enums.GroupType.CLAN, 771 ) -> collections.Sequence[clans.GroupMember]: 772 """Fetch the potential groups for a clan member. 773 774 Parameters 775 ---------- 776 member_id : `int` 777 The member's id 778 member_type : `aiobungie.aiobungie.MembershipType | int` 779 The member's membership type. 780 781 Other Parameters 782 ---------------- 783 filter : `int` 784 Filter apply to list of joined groups. This Default to `0` 785 group_type : `aiobungie.aiobungie.GroupType | int` 786 The group's type. 787 This is always set to `aiobungie.GroupType.CLAN` and should not be changed. 788 789 Returns 790 ------- 791 `collections.Sequence[aiobungie.crates.GroupMember]` 792 A sequence of joined potential groups for the fetched member. 793 """ 794 resp = await self.rest.fetch_potential_groups_for_member( 795 member_id, member_type, filter=filter, group_type=group_type 796 ) 797 798 return tuple( 799 self.factory.deserialize_group_member(group) for group in resp["results"] 800 ) 801 802 async def fetch_clan_members( 803 self, 804 clan_id: int, 805 /, 806 *, 807 name: str | None = None, 808 type: enums.MembershipType | int = enums.MembershipType.NONE, 809 ) -> iterators.Iterator[clans.ClanMember]: 810 """Fetch Bungie clan members. 811 812 Parameters 813 ---------- 814 clan_id : `int` 815 The clans id 816 817 Other Parameters 818 ---------------- 819 name : `str | None` 820 If provided, Only players matching this name will be returned. 821 type : `aiobungie.MembershipType` 822 An optional clan member's membership type. 823 This parameter is used to filter the returned results 824 by the provided membership, For an example XBox memberships only, 825 Otherwise will return all memberships. 826 827 Returns 828 ------- 829 `aiobungie.Iterator[aiobungie.crates.ClanMember]` 830 An iterator over the bungie clan members. 831 832 Raises 833 ------ 834 `aiobungie.NotFound` 835 The clan was not found. 836 """ 837 resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name) 838 839 return self.factory.deserialize_clan_members(resp) 840 841 async def fetch_clan_banners(self) -> collections.Sequence[clans.ClanBanner]: 842 """Fetch the clan banners. 843 844 Returns 845 ------- 846 `collections.Sequence[aiobungie.crates.ClanBanner]` 847 A sequence of the clan banners. 848 """ 849 resp = await self.rest.fetch_clan_banners() 850 851 return self.factory.deserialize_clan_banners(resp) 852 853 # This method is required to be here since it deserialize the clan. 854 async def kick_clan_member( 855 self, 856 access_token: str, 857 /, 858 group_id: int, 859 membership_id: int, 860 membership_type: enums.MembershipType | int, 861 ) -> clans.Clan: 862 """Kick a member from the clan. 863 864 .. note:: 865 This request requires OAuth2: oauth2: `AdminGroups` scope. 866 867 Parameters 868 ---------- 869 access_token : `str` 870 The bearer access token associated with the bungie account. 871 group_id: `int` 872 The group id. 873 membership_id : `int` 874 The member id to kick. 875 membership_type : `aiobungie.aiobungie.MembershipType | int` 876 The member's membership type. 877 878 Returns 879 ------- 880 `aiobungie.crates.clan.Clan` 881 The clan that the member was kicked from. 882 """ 883 resp = await self.rest.kick_clan_member( 884 access_token, 885 group_id=group_id, 886 membership_id=membership_id, 887 membership_type=membership_type, 888 ) 889 890 return self.factory.deserialize_clan(resp) 891 892 async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone: 893 """Fetch a Bungie clan's weekly reward state. 894 895 Parameters 896 ---------- 897 clan_id : `int` 898 The clan's id. 899 900 Returns 901 ------- 902 `aiobungie.crates.Milestone` 903 A runtime status of the clan's milestone data. 904 """ 905 906 resp = await self.rest.fetch_clan_weekly_rewards(clan_id) 907 908 return self.factory.deserialize_milestone(resp) 909 910 # * Destiny 2 Entities aka Definitions. 911 912 async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity: 913 """Fetch a static inventory item entity given a its hash. 914 915 Parameters 916 ---------- 917 hash: `int` 918 Inventory item's hash. 919 920 Returns 921 ------- 922 `aiobungie.crates.InventoryEntity` 923 A bungie inventory item. 924 """ 925 resp = await self.rest.fetch_inventory_item(hash) 926 927 return self.factory.deserialize_inventory_entity(resp) 928 929 async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity: 930 """Fetch a Destiny objective entity given a its hash. 931 932 Parameters 933 ---------- 934 hash: `int` 935 objective's hash. 936 937 Returns 938 ------- 939 `aiobungie.crates.ObjectiveEntity` 940 An objective entity item. 941 """ 942 resp = await self.rest.fetch_objective_entity(hash) 943 944 return self.factory.deserialize_objective_entity(resp) 945 946 async def search_entities( 947 self, name: str, entity_type: str, *, page: int = 0 948 ) -> iterators.Iterator[entity.SearchableEntity]: 949 """Search for Destiny2 entities given a name and its type. 950 951 Parameters 952 ---------- 953 name : `str` 954 The name of the entity, i.e., Thunderlord, One thousand voices. 955 entity_type : `str` 956 The type of the entity, AKA Definition, 957 For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items. 958 959 Other Parameters 960 ---------------- 961 page : `int` 962 An optional page to return. Default to 0. 963 964 Returns 965 ------- 966 `aiobungie.Iterator[aiobungie.crates.SearchableEntity]` 967 An iterator over the found results matching the provided name. 968 """ 969 resp = await self.rest.search_entities(name, entity_type, page=page) 970 971 return self.factory.deserialize_inventory_results(resp) 972 973 # Fireteams 974 975 async def fetch_fireteams( 976 self, 977 activity_type: fireteams.FireteamActivity | int, 978 *, 979 platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY, 980 language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL, 981 date_range: int = 0, 982 page: int = 0, 983 slots_filter: int = 0, 984 ) -> collections.Sequence[fireteams.Fireteam]: 985 """Fetch public Bungie fireteams with open slots. 986 987 Parameters 988 ---------- 989 activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int` 990 The fireteam activity type. 991 992 Other Parameters 993 ---------------- 994 platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int` 995 If this is provided. Then the results will be filtered with the given platform. 996 Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms. 997 language : `aiobungie.crates.fireteams.FireteamLanguage | str` 998 A locale language to filter the used language in that fireteam. 999 Defaults to `aiobungie.crates.FireteamLanguage.ALL` 1000 date_range : `int` 1001 An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`. 1002 page : `int` 1003 The page number. By default its `0` which returns all available activities. 1004 slots_filter : `int` 1005 Filter the returned fireteams based on available slots. Default is `0` 1006 1007 Returns 1008 ------- 1009 `collections.Sequence[fireteams.Fireteam]` 1010 A sequence of `aiobungie.crates.Fireteam`. 1011 """ 1012 1013 resp = await self.rest.fetch_fireteams( 1014 activity_type, 1015 platform=platform, 1016 language=language, 1017 date_range=date_range, 1018 page=page, 1019 slots_filter=slots_filter, 1020 ) 1021 1022 return self.factory.deserialize_fireteams(resp) 1023 1024 async def fetch_available_clan_fireteams( 1025 self, 1026 access_token: str, 1027 group_id: int, 1028 activity_type: fireteams.FireteamActivity | int, 1029 *, 1030 platform: fireteams.FireteamPlatform | int, 1031 language: fireteams.FireteamLanguage | str, 1032 date_range: int = 0, 1033 page: int = 0, 1034 public_only: bool = False, 1035 slots_filter: int = 0, 1036 ) -> collections.Sequence[fireteams.Fireteam]: 1037 """Fetch a clan's fireteams with open slots. 1038 1039 .. note:: 1040 This method requires OAuth2: ReadGroups scope. 1041 1042 Parameters 1043 ---------- 1044 access_token : `str` 1045 The bearer access token associated with the bungie account. 1046 group_id : `int` 1047 The group/clan id of the fireteam. 1048 activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int` 1049 The fireteam activity type. 1050 1051 Other Parameters 1052 ---------------- 1053 platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int` 1054 If this is provided. Then the results will be filtered with the given platform. 1055 Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms. 1056 language : `aiobungie.crates.fireteams.FireteamLanguage | str` 1057 A locale language to filter the used language in that fireteam. 1058 Defaults to `aiobungie.crates.FireteamLanguage.ALL` 1059 date_range : `int` 1060 An integer to filter the date range of the returned fireteams. Defaults to `0`. 1061 page : `int` 1062 The page number. By default its `0` which returns all available activities. 1063 public_only: `bool` 1064 If set to True, Then only public fireteams will be returned. 1065 slots_filter : `int` 1066 Filter the returned fireteams based on available slots. Default is `0` 1067 1068 Returns 1069 ------- 1070 `collections.Sequence[aiobungie.crates.Fireteam]` 1071 A sequence of fireteams found in the clan. 1072 `None` will be returned if nothing was found. 1073 """ 1074 resp = await self.rest.fetch_available_clan_fireteams( 1075 access_token, 1076 group_id, 1077 activity_type, 1078 platform=platform, 1079 language=language, 1080 date_range=date_range, 1081 page=page, 1082 public_only=public_only, 1083 slots_filter=slots_filter, 1084 ) 1085 1086 return self.factory.deserialize_fireteams(resp) 1087 1088 async def fetch_clan_fireteam( 1089 self, access_token: str, fireteam_id: int, group_id: int 1090 ) -> fireteams.AvailableFireteam: 1091 """Fetch a specific clan fireteam. 1092 1093 .. note:: 1094 This method requires OAuth2: ReadGroups scope. 1095 1096 Parameters 1097 ---------- 1098 access_token : `str` 1099 The bearer access token associated with the bungie account. 1100 group_id : `int` 1101 The group/clan id to fetch the fireteam from. 1102 fireteam_id : `int` 1103 The fireteam id to fetch. 1104 1105 Returns 1106 ------- 1107 `aiobungie.crates.AvailableFireteam` 1108 A sequence of available fireteams objects. 1109 """ 1110 resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id) 1111 1112 return self.factory.deserialize_available_fireteam(resp) 1113 1114 async def fetch_my_clan_fireteams( 1115 self, 1116 access_token: str, 1117 group_id: int, 1118 *, 1119 include_closed: bool = True, 1120 platform: fireteams.FireteamPlatform | int, 1121 language: fireteams.FireteamLanguage | str, 1122 filtered: bool = True, 1123 page: int = 0, 1124 ) -> collections.Sequence[fireteams.AvailableFireteam]: 1125 """A method that's similar to `fetch_fireteams` but requires OAuth2. 1126 1127 .. note:: 1128 This method requires OAuth2: ReadGroups scope. 1129 1130 Parameters 1131 ---------- 1132 access_token : str 1133 The bearer access token associated with the bungie account. 1134 group_id : int 1135 The group/clan id to fetch. 1136 1137 Other Parameters 1138 ---------------- 1139 include_closed : `bool` 1140 If provided and set to True, It will also return closed fireteams. 1141 If provided and set to False, It will only return public fireteams. Default is True. 1142 platform : aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int 1143 If this is provided. Then the results will be filtered with the given platform. 1144 Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms. 1145 language : `aiobungie.crates.fireteams.FireteamLanguage | str` 1146 A locale language to filter the used language in that fireteam. 1147 Defaults to aiobungie.crates.FireteamLanguage.ALL 1148 filtered : `bool` 1149 If set to True, it will filter by clan. Otherwise not. Default is True. 1150 page : `int` 1151 The page number. By default its 0 which returns all available activities. 1152 1153 Returns 1154 ------- 1155 `collections.Sequence[aiobungie.crates.AvailableFireteam]` 1156 A sequence of available fireteams objects if exists. else `None` will be returned. 1157 """ 1158 resp = await self.rest.fetch_my_clan_fireteams( 1159 access_token, 1160 group_id, 1161 include_closed=include_closed, 1162 platform=platform, 1163 language=language, 1164 filtered=filtered, 1165 page=page, 1166 ) 1167 1168 return self.factory.deserialize_available_fireteams(resp) 1169 1170 # Friends and social. 1171 1172 async def fetch_friends( 1173 self, access_token: str, / 1174 ) -> collections.Sequence[friends.Friend]: 1175 """Fetch bungie friend list. 1176 1177 .. note:: 1178 This requests OAuth2: ReadUserData scope. 1179 1180 Parameters 1181 ----------- 1182 access_token : `str` 1183 The bearer access token associated with the bungie account. 1184 1185 Returns 1186 ------- 1187 `collections.Sequence[aiobungie.crates.Friend]` 1188 A sequence of the friends associated with that access token. 1189 """ 1190 1191 resp = await self.rest.fetch_friends(access_token) 1192 1193 return self.factory.deserialize_friends(resp) 1194 1195 async def fetch_friend_requests( 1196 self, access_token: str, / 1197 ) -> friends.FriendRequestView: 1198 """Fetch pending bungie friend requests queue. 1199 1200 .. note:: 1201 This requests OAuth2: ReadUserData scope. 1202 1203 Parameters 1204 ----------- 1205 access_token : `str` 1206 The bearer access token associated with the bungie account. 1207 1208 Returns 1209 ------- 1210 `aiobungie.crates.FriendRequestView` 1211 A friend requests view of that associated access token. 1212 """ 1213 1214 resp = await self.rest.fetch_friend_requests(access_token) 1215 1216 return self.factory.deserialize_friend_requests(resp) 1217 1218 # Applications and Developer portal. 1219 1220 async def fetch_application(self, appid: int, /) -> application.Application: 1221 """Fetch a Bungie application. 1222 1223 Parameters 1224 ----------- 1225 appid: `int` 1226 The application id. 1227 1228 Returns 1229 -------- 1230 `aiobungie.crates.Application` 1231 A Bungie application. 1232 """ 1233 resp = await self.rest.fetch_application(appid) 1234 1235 return self.factory.deserialize_app(resp) 1236 1237 # Milestones 1238 1239 async def fetch_public_milestone_content( 1240 self, milestone_hash: int, / 1241 ) -> milestones.MilestoneContent: 1242 """Fetch the milestone content given its hash. 1243 1244 Parameters 1245 ---------- 1246 milestone_hash : `int` 1247 The milestone hash. 1248 1249 Returns 1250 ------- 1251 `aiobungie.crates.milestones.MilestoneContent` 1252 A milestone content object. 1253 """ 1254 ... 1255 resp = await self.rest.fetch_public_milestone_content(milestone_hash) 1256 return self.factory.deserialize_public_milestone_content(resp)
Standard Bungie API client implementation.
This client deserialize the REST JSON responses using aiobungie.Factory
and returns aiobungie.crates Python object implementations of the responses.
Example
import aiobungie
client = aiobungie.Client('...')
async def main():
async with client.rest:
user = await client.fetch_current_user_memberships('...')
print(user)
Parameters
- token (
str): Your Bungie's API key or Token from the developer's portal.
Other Parameters
- max_retries (
int): The max retries number to retry if the request hit a5xxstatus code. - client_secret (
str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client. - client_id (
int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client. - debug (
"TRACE" | bool | int): The level of logging to enable.
95 def __init__( 96 self, 97 token: str, 98 /, 99 *, 100 client_secret: str | None = None, 101 client_id: int | None = None, 102 max_retries: int = 4, 103 debug: typing.Literal["TRACE"] | bool | int = False, 104 ) -> None: 105 self._rest = rest_.RESTClient( 106 token, 107 client_secret=client_secret, 108 client_id=client_id, 109 max_retries=max_retries, 110 debug=debug, 111 ) 112 113 self._factory = factory_.Factory(self)
127 @property 128 def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: 129 return self._rest.metadata
A mutable mapping storage for the user's needs.
131 def run(self, fn: collections.Awaitable[typing.Any], debug: bool = False) -> None: 132 loop = helpers.get_or_make_loop() 133 134 try: 135 if not loop.is_running(): 136 loop.set_debug(debug) 137 loop.run_until_complete(fn) 138 139 except Exception as exc: 140 raise RuntimeError(f"Failed to run {fn!s}") from exc
Runs a coroutine function until its complete.
This is equivalent to asyncio.get_event_loop().run_until_complete(...)
Parameters
- fn (
collections.Awaitable[Any]): The async function to run. - debug (
bool): Either to enable asyncio debug or not. Disabled by default.
Example
async def main() -> None:
await fetch(...)
# Run the coroutine.
client.run(main())
144 async def fetch_current_user_memberships(self, access_token: str, /) -> user.User: 145 """Fetch and return a user object of the bungie net user associated with account. 146 147 .. warning:: 148 This method requires OAuth2 scope and a Bearer access token. 149 150 Parameters 151 ---------- 152 access_token : `str` 153 A valid Bearer access token for the authorization. 154 155 Returns 156 ------- 157 `aiobungie.crates.user.User` 158 A user object includes the Destiny memberships and Bungie.net user. 159 """ 160 resp = await self.rest.fetch_current_user_memberships(access_token) 161 162 return self.factory.deserialize_user(resp)
Fetch and return a user object of the bungie net user associated with account.
This method requires OAuth2 scope and a Bearer access token.
Parameters
- access_token (
str): A valid Bearer access token for the authorization.
Returns
aiobungie.crates.user.User: A user object includes the Destiny memberships and Bungie.net user.
164 async def fetch_bungie_user(self, id: int, /) -> user.BungieUser: 165 """Fetch a Bungie user by their BungieNet id. 166 167 .. note:: 168 This returns a Bungie user membership only. Take a look at `Client.fetch_membership_from_id` 169 for other memberships. 170 171 Parameters 172 ---------- 173 id: `int` 174 The user id. 175 176 Returns 177 ------- 178 `aiobungie.crates.user.BungieUser` 179 A Bungie user. 180 181 Raises 182 ------ 183 `aiobungie.error.NotFound` 184 The user was not found. 185 """ 186 payload = await self.rest.fetch_bungie_user(id) 187 188 return self.factory.deserialize_bungie_user(payload)
Fetch a Bungie user by their BungieNet id.
This returns a Bungie user membership only. Take a look at Client.fetch_membership_from_id
for other memberships.
Parameters
- id (
int): The user id.
Returns
aiobungie.crates.user.BungieUser: A Bungie user.
Raises
NotFound: The user was not found.
190 async def search_users( 191 self, name: str, / 192 ) -> iterators.Iterator[user.SearchableDestinyUser]: 193 """Search for players and return all players that matches the same name. 194 195 Parameters 196 ---------- 197 name : `str` 198 The user name. 199 200 Returns 201 ------- 202 `aiobungie.Iterator[aiobungie.crates.SearchableDestinyUser]` 203 A sequence of the found users with this name. 204 """ 205 payload = await self.rest.search_users(name) 206 207 return iterators.Iterator( 208 [ 209 self.factory.deserialize_searched_user(user) 210 for user in payload["searchResults"] 211 ] 212 )
Search for players and return all players that matches the same name.
Parameters
- name (
str): The user name.
Returns
aiobungie.Iterator[aiobungie.crates.SearchableDestinyUser]: A sequence of the found users with this name.
214 async def fetch_user_themes(self) -> collections.Sequence[user.UserThemes]: 215 """Fetch all available user themes. 216 217 Returns 218 ------- 219 `collections.Sequence[aiobungie.crates.user.UserThemes]` 220 A sequence of user themes. 221 """ 222 data = await self.rest.fetch_user_themes() 223 224 return self.factory.deserialize_user_themes(data)
Fetch all available user themes.
Returns
collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of user themes.
226 async def fetch_hard_types( 227 self, 228 credential: int, 229 type: enums.CredentialType | int = enums.CredentialType.STEAMID, 230 /, 231 ) -> user.HardLinkedMembership: 232 """Gets any hard linked membership given a credential. 233 Only works for credentials that are public just `aiobungie.CredentialType.STEAMID` right now. 234 Cross Save aware. 235 236 Parameters 237 ---------- 238 credential: `int` 239 A valid SteamID64 240 type: `aiobungie.CredentialType` 241 The credential type. This must not be changed 242 Since its only credential that works "currently" 243 244 Returns 245 ------- 246 `aiobungie.crates.user.HardLinkedMembership` 247 Information about the hard linked data. 248 """ 249 250 payload = await self.rest.fetch_hardlinked_credentials(credential, type) 251 252 return user.HardLinkedMembership( 253 id=int(payload["membershipId"]), 254 type=enums.MembershipType(payload["membershipType"]), 255 cross_save_type=enums.MembershipType(payload["CrossSaveOverriddenType"]), 256 )
Gets any hard linked membership given a credential.
Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now.
Cross Save aware.
Parameters
- credential (
int): A valid SteamID64 - type (
aiobungie.CredentialType): The credential type. This must not be changed Since its only credential that works "currently"
Returns
aiobungie.crates.user.HardLinkedMembership: Information about the hard linked data.
258 async def fetch_membership_from_id( 259 self, 260 id: int, 261 /, 262 type: enums.MembershipType | int = enums.MembershipType.NONE, 263 ) -> user.User: 264 """Fetch Bungie user's memberships from their id. 265 266 Notes 267 ----- 268 * This returns both BungieNet membership and a sequence of the player's DestinyMemberships 269 Which includes Stadia, Xbox, Steam and PSN memberships if the player has them, 270 see `aiobungie.crates.user.DestinyMembership` for more details. 271 * If you only want the bungie user. Consider using `Client.fetch_user` method. 272 273 Parameters 274 ---------- 275 id : `int` 276 The user's id. 277 type : `aiobungie.MembershipType` 278 The user's membership type. 279 280 Returns 281 ------- 282 `aiobungie.crates.User` 283 A Bungie user with their membership types. 284 285 Raises 286 ------ 287 aiobungie.NotFound 288 The requested user was not found. 289 """ 290 payload = await self.rest.fetch_membership_from_id(id, type) 291 292 return self.factory.deserialize_user(payload)
Fetch Bungie user's memberships from their id.
Notes
- This returns both BungieNet membership and a sequence of the player's DestinyMemberships
Which includes Stadia, Xbox, Steam and PSN memberships if the player has them,
see
aiobungie.crates.user.DestinyMembershipfor more details. - If you only want the bungie user. Consider using
Client.fetch_usermethod.
Parameters
- id (
int): The user's id. - type (
aiobungie.MembershipType): The user's membership type.
Returns
aiobungie.crates.User: A Bungie user with their membership types.
Raises
- aiobungie.NotFound: The requested user was not found.
294 async def fetch_user_credentials( 295 self, access_token: str, membership_id: int, / 296 ) -> collections.Sequence[user.UserCredentials]: 297 """Fetch an array of credential types attached to the requested account. 298 299 .. note:: 300 This method require OAuth2 Bearer access token. 301 302 Parameters 303 ---------- 304 access_token : `str` 305 The bearer access token associated with the bungie account. 306 membership_id : `int` 307 The id of the membership to return. 308 309 Returns 310 ------- 311 `collections.Sequence[aiobungie.crates.UserCredentials]` 312 A sequence of the attached user credentials. 313 314 Raises 315 ------ 316 `aiobungie.Unauthorized` 317 The access token was wrong or no access token passed. 318 """ 319 resp = await self.rest.fetch_user_credentials(access_token, membership_id) 320 321 return self.factory.deserialize_user_credentials(resp)
Fetch an array of credential types attached to the requested account.
This method require OAuth2 Bearer access token.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - membership_id (
int): The id of the membership to return.
Returns
collections.Sequence[aiobungie.crates.UserCredentials]: A sequence of the attached user credentials.
Raises
aiobungie.Unauthorized: The access token was wrong or no access token passed.
325 async def fetch_profile( 326 self, 327 member_id: int, 328 type: enums.MembershipType | int, 329 components: collections.Sequence[enums.ComponentType], 330 auth: str | None = None, 331 ) -> components.Component: 332 """ 333 Fetch a bungie profile passing components to the request. 334 335 Parameters 336 ---------- 337 member_id: `int` 338 The member's id. 339 type: `aiobungie.MembershipType` 340 A valid membership type. 341 components : `collections.Sequence[aiobungie.ComponentType]` 342 List of profile components to collect and return. 343 344 Other Parameters 345 ---------------- 346 auth : `str | None` 347 A Bearer access_token to make the request with. 348 This is optional and limited to components that only requires an Authorization token. 349 350 Returns 351 -------- 352 `aiobungie.crates.Component` 353 A Destiny 2 player profile with its components. 354 Only passed components will be available if they exists. Otherwise they will be `None` 355 356 Raises 357 ------ 358 `aiobungie.MembershipTypeError` 359 The provided membership type was invalid. 360 """ 361 data = await self.rest.fetch_profile(member_id, type, components, auth) 362 return self.factory.deserialize_components(data)
Fetch a bungie profile passing components to the request.
Parameters
- member_id (
int): The member's id. - type (
aiobungie.MembershipType): A valid membership type. - components (
collections.Sequence[aiobungie.ComponentType]): List of profile components to collect and return.
Other Parameters
- auth (
str | None): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
aiobungie.crates.Component: A Destiny 2 player profile with its components. Only passed components will be available if they exists. Otherwise they will beNone
Raises
aiobungie.MembershipTypeError: The provided membership type was invalid.
364 async def fetch_linked_profiles( 365 self, 366 member_id: int, 367 member_type: enums.MembershipType | int, 368 /, 369 *, 370 all: bool = False, 371 ) -> profile.LinkedProfile: 372 """Returns a summary information about all profiles linked to the requested member. 373 374 The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships. 375 376 .. note:: 377 It will only return linked accounts whose linkages you are allowed to view. 378 379 Parameters 380 ---------- 381 member_id : `int` 382 The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID. 383 member_type : `aiobungie.MembershipType` 384 The type for the membership whose linked Destiny account you want to return. 385 386 Other Parameters 387 ---------------- 388 all : `bool` 389 If provided and set to `True`, All memberships regardless 390 of whether they're obscured by overrides will be returned, 391 392 If provided and set to `False`, Only available memberships will be returned. 393 The default for this is `False`. 394 395 Returns 396 ------- 397 `aiobungie.crates.profile.LinkedProfile` 398 A linked profile object. 399 """ 400 resp = await self.rest.fetch_linked_profiles(member_id, member_type, all=all) 401 402 return self.factory.deserialize_linked_profiles(resp)
Returns a summary information about all profiles linked to the requested member.
The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
It will only return linked accounts whose linkages you are allowed to view.
Parameters
- member_id (
int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID. - member_type (
aiobungie.MembershipType): The type for the membership whose linked Destiny account you want to return.
Other Parameters
all (
bool): If provided and set toTrue, All memberships regardless of whether they're obscured by overrides will be returned,If provided and set to
False, Only available memberships will be returned. The default for this isFalse.
Returns
aiobungie.crates.profile.LinkedProfile: A linked profile object.
404 async def fetch_membership( 405 self, 406 name: str, 407 code: int, 408 /, 409 type: enums.MembershipType | int = enums.MembershipType.ALL, 410 ) -> collections.Sequence[user.DestinyMembership]: 411 """Fetch a Destiny 2 player's memberships. 412 413 Parameters 414 ----------- 415 name: `str` 416 The unique Bungie player name. 417 code : `int` 418 The unique Bungie display name code. 419 type: `aiobungie.internal.enums.MembershipType` 420 The player's membership type, e,g. XBOX, STEAM, PSN 421 422 Returns 423 -------- 424 `collections.Sequence[aiobungie.crates.DestinyMembership]` 425 A sequence of the found Destiny 2 player memberships. 426 An empty sequence will be returned if no one found. 427 428 Raises 429 ------ 430 `aiobungie.MembershipTypeError` 431 The provided membership type was invalid. 432 """ 433 resp = await self.rest.fetch_membership(name, code, type) 434 435 return self.factory.deserialize_destiny_memberships(resp)
Fetch a Destiny 2 player's memberships.
Parameters
- name (
str): The unique Bungie player name. - code (
int): The unique Bungie display name code. - type (
MembershipType): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
collections.Sequence[aiobungie.crates.DestinyMembership]: A sequence of the found Destiny 2 player memberships. An empty sequence will be returned if no one found.
Raises
aiobungie.MembershipTypeError: The provided membership type was invalid.
437 async def fetch_character( 438 self, 439 member_id: int, 440 membership_type: enums.MembershipType | int, 441 character_id: int, 442 components: collections.Sequence[enums.ComponentType], 443 auth: str | None = None, 444 ) -> components.CharacterComponent: 445 """Fetch a Destiny 2 character. 446 447 Parameters 448 ---------- 449 member_id: `int` 450 A valid bungie member id. 451 character_id: `int` 452 The Destiny character id to retrieve. 453 membership_type: `aiobungie.internal.enums.MembershipType` 454 The member's membership type. 455 components: `collections.Sequence[aiobungie.ComponentType]` 456 Multiple arguments of character components to collect and return. 457 458 Other Parameters 459 ---------------- 460 auth : `str | None` 461 A Bearer access_token to make the request with. 462 This is optional and limited to components that only requires an Authorization token. 463 464 Returns 465 ------- 466 `aiobungie.crates.CharacterComponent` 467 A Bungie character component. 468 469 `aiobungie.MembershipTypeError` 470 The provided membership type was invalid. 471 """ 472 resp = await self.rest.fetch_character( 473 member_id, membership_type, character_id, components, auth 474 ) 475 476 return self.factory.deserialize_character_component(resp)
Fetch a Destiny 2 character.
Parameters
- member_id (
int): A valid bungie member id. - character_id (
int): The Destiny character id to retrieve. - membership_type (
MembershipType): The member's membership type. - components (
collections.Sequence[aiobungie.ComponentType]): Multiple arguments of character components to collect and return.
Other Parameters
- auth (
str | None): A Bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
aiobungie.crates.CharacterComponent: A Bungie character component.aiobungie.MembershipTypeError: The provided membership type was invalid.
478 async def fetch_unique_weapon_history( 479 self, 480 membership_id: int, 481 character_id: int, 482 membership_type: enums.MembershipType | int, 483 ) -> collections.Sequence[activity.ExtendedWeaponValues]: 484 """Fetch details about unique weapon usage for a character. Includes all exotics. 485 486 Parameters 487 ---------- 488 membership_id : `int` 489 The Destiny user membership id. 490 character_id : `int` 491 The character id to retrieve. 492 membership_type : `aiobungie.aiobungie.MembershipType | int` 493 The Destiny user's membership type. 494 495 Returns 496 ------- 497 `collections.Sequence[aiobungie.crates.ExtendedWeaponValues]` 498 A sequence of the weapon's extended values. 499 """ 500 resp = await self._rest.fetch_unique_weapon_history( 501 membership_id, character_id, membership_type 502 ) 503 504 return tuple( 505 self._factory.deserialize_extended_weapon_values(weapon) 506 for weapon in resp["weapons"] 507 )
Fetch details about unique weapon usage for a character. Includes all exotics.
Parameters
- membership_id (
int): The Destiny user membership id. - character_id (
int): The character id to retrieve. - membership_type (
aiobungie.aiobungie.MembershipType | int): The Destiny user's membership type.
Returns
collections.Sequence[aiobungie.crates.ExtendedWeaponValues]: A sequence of the weapon's extended values.
511 async def fetch_activities( 512 self, 513 member_id: int, 514 character_id: int, 515 mode: enums.GameMode | int, 516 *, 517 membership_type: enums.MembershipType | int = enums.MembershipType.ALL, 518 page: int = 0, 519 limit: int = 250, 520 ) -> iterators.Iterator[activity.Activity]: 521 """Fetch a Destiny 2 activity for the specified character id. 522 523 Parameters 524 ---------- 525 member_id: `int` 526 The user id that starts with `4611`. 527 character_id: `int` 528 The id of the character to retrieve the activities for. 529 mode: `aiobungie.aiobungie.internal.enums.GameMode | int` 530 This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc. 531 532 Other Parameters 533 ---------------- 534 membership_type: `aiobungie.internal.enums.MembershipType` 535 The Member ship type, if nothing was passed than it will return all. 536 page: int 537 The page number. Default is `0` 538 limit: int 539 Limit the returned result. Default is `250`. 540 541 Returns 542 ------- 543 `aiobungie.Iterator[aiobungie.crates.Activity]` 544 An iterator of the player's activities. 545 546 Raises 547 ------ 548 `aiobungie.MembershipTypeError` 549 The provided membership type was invalid. 550 """ 551 resp = await self.rest.fetch_activities( 552 member_id, 553 character_id, 554 mode, 555 membership_type=membership_type, 556 page=page, 557 limit=limit, 558 ) 559 560 return self.factory.deserialize_activities(resp)
Fetch a Destiny 2 activity for the specified character id.
Parameters
- member_id (
int): The user id that starts with4611. - character_id (
int): The id of the character to retrieve the activities for. - mode (
aiobungie.aiobungie.internal.enums.GameMode | int): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
- membership_type (
MembershipType): The Member ship type, if nothing was passed than it will return all. - page (int):
The page number. Default is
0 - limit (int):
Limit the returned result. Default is
250.
Returns
aiobungie.Iterator[aiobungie.crates.Activity]: An iterator of the player's activities.
Raises
aiobungie.MembershipTypeError: The provided membership type was invalid.
562 async def fetch_post_activity(self, instance_id: int, /) -> activity.PostActivity: 563 """Fetch a post activity details. 564 565 Parameters 566 ---------- 567 instance_id: `int` 568 The activity instance id. 569 570 Returns 571 ------- 572 `aiobungie.crates.PostActivity` 573 A post activity object. 574 """ 575 resp = await self.rest.fetch_post_activity(instance_id) 576 577 return self.factory.deserialize_post_activity(resp)
Fetch a post activity details.
Parameters
- instance_id (
int): The activity instance id.
Returns
aiobungie.crates.PostActivity: A post activity object.
579 async def fetch_aggregated_activity_stats( 580 self, 581 character_id: int, 582 membership_id: int, 583 membership_type: enums.MembershipType | int, 584 ) -> iterators.Iterator[activity.AggregatedActivity]: 585 """Fetch aggregated activity stats for a character. 586 587 Parameters 588 ---------- 589 character_id: `int` 590 The id of the character to retrieve the activities for. 591 membership_id: `int` 592 The id of the user that started with `4611`. 593 membership_type: `aiobungie.internal.enums.MembershipType` 594 The Member ship type. 595 596 Returns 597 ------- 598 `aiobungie.Iterator[aiobungie.crates.AggregatedActivity]` 599 An iterator of the player's activities. 600 601 Raises 602 ------ 603 `aiobungie.MembershipTypeError` 604 The provided membership type was invalid. 605 """ 606 resp = await self.rest.fetch_aggregated_activity_stats( 607 character_id, membership_id, membership_type 608 ) 609 610 return self.factory.deserialize_aggregated_activities(resp)
Fetch aggregated activity stats for a character.
Parameters
- character_id (
int): The id of the character to retrieve the activities for. - membership_id (
int): The id of the user that started with4611. - membership_type (
MembershipType): The Member ship type.
Returns
aiobungie.Iterator[aiobungie.crates.AggregatedActivity]: An iterator of the player's activities.
Raises
aiobungie.MembershipTypeError: The provided membership type was invalid.
614 async def fetch_clan_from_id( 615 self, 616 id: int, 617 /, 618 access_token: str | None = None, 619 ) -> clans.Clan: 620 """Fetch a Bungie Clan by its id. 621 622 Parameters 623 ----------- 624 id: `int` 625 The clan id. 626 627 Returns 628 -------- 629 `aiobungie.crates.Clan` 630 An Bungie clan. 631 632 Raises 633 ------ 634 `aiobungie.NotFound` 635 The clan was not found. 636 """ 637 resp = await self.rest.fetch_clan_from_id(id, access_token) 638 639 return self.factory.deserialize_clan(resp)
Fetch a Bungie Clan by its id.
Parameters
- id (
int): The clan id.
Returns
aiobungie.crates.Clan: An Bungie clan.
Raises
aiobungie.NotFound: The clan was not found.
641 async def fetch_clan( 642 self, 643 name: str, 644 /, 645 access_token: str | None = None, 646 *, 647 type: enums.GroupType | int = enums.GroupType.CLAN, 648 ) -> clans.Clan: 649 """Fetch a Clan by its name. 650 This method will return the first clan found with given name. 651 652 Parameters 653 ---------- 654 name: `str` 655 The clan name 656 657 Other Parameters 658 ---------------- 659 access_token : `str | None` 660 An optional access token to make the request with. 661 662 If the token was bound to a member of the clan, 663 This field `aiobungie.crates.Clan.current_user_membership` will be available 664 and will return the membership of the user who made this request. 665 type : `aiobungie.GroupType` 666 The group type, Default is aiobungie.GroupType.CLAN. 667 668 Returns 669 ------- 670 `aiobungie.crates.Clan` 671 A Bungie clan. 672 673 Raises 674 ------ 675 `aiobungie.NotFound` 676 The clan was not found. 677 """ 678 resp = await self.rest.fetch_clan(name, access_token, type=type) 679 680 return self.factory.deserialize_clan(resp)
Fetch a Clan by its name. This method will return the first clan found with given name.
Parameters
- name (
str): The clan name
Other Parameters
access_token (
str | None): An optional access token to make the request with.If the token was bound to a member of the clan, This field
aiobungie.crates.Clan.current_user_membershipwill be available and will return the membership of the user who made this request.- type (
aiobungie.GroupType): The group type, Default is aiobungie.GroupType.CLAN.
Returns
aiobungie.crates.Clan: A Bungie clan.
Raises
aiobungie.NotFound: The clan was not found.
682 async def fetch_clan_conversations( 683 self, clan_id: int, / 684 ) -> collections.Sequence[clans.ClanConversation]: 685 """Fetch the conversations/chat channels of the given clan id. 686 687 Parameters 688 ---------- 689 clan_id : `int` 690 The clan id. 691 692 Returns 693 `collections.Sequence[aiobungie.crates.ClanConversation]` 694 A sequence of the clan chat channels. 695 """ 696 resp = await self.rest.fetch_clan_conversations(clan_id) 697 698 return self.factory.deserialize_clan_conversations(resp)
Fetch the conversations/chat channels of the given clan id.
Parameters
- clan_id (
int): The clan id. - Returns
collections.Sequence[aiobungie.crates.ClanConversation]: A sequence of the clan chat channels.
700 async def fetch_clan_admins( 701 self, clan_id: int, / 702 ) -> iterators.Iterator[clans.ClanMember]: 703 """Fetch the clan founder and admins. 704 705 Parameters 706 ---------- 707 clan_id : `int` 708 The clan id. 709 710 Returns 711 ------- 712 `aiobungie.Iterator[aiobungie.crates.ClanMember]` 713 An iterator over the found clan admins and founder. 714 715 Raises 716 ------ 717 `aiobungie.NotFound` 718 The requested clan was not found. 719 """ 720 resp = await self.rest.fetch_clan_admins(clan_id) 721 722 return self.factory.deserialize_clan_members(resp)
Fetch the clan founder and admins.
Parameters
- clan_id (
int): The clan id.
Returns
aiobungie.Iterator[aiobungie.crates.ClanMember]: An iterator over the found clan admins and founder.
Raises
aiobungie.NotFound: The requested clan was not found.
724 async def fetch_groups_for_member( 725 self, 726 member_id: int, 727 member_type: enums.MembershipType | int, 728 /, 729 *, 730 filter: int = 0, 731 group_type: enums.GroupType = enums.GroupType.CLAN, 732 ) -> collections.Sequence[clans.GroupMember]: 733 """Fetch information about the groups that a given member has joined. 734 735 Parameters 736 ---------- 737 member_id : `int` 738 The member's id 739 member_type : `aiobungie.MembershipType` 740 The member's membership type. 741 742 Other Parameters 743 ---------------- 744 filter : `int` 745 Filter apply to list of joined groups. This Default to `0` 746 group_type : `aiobungie.GroupType` 747 The group's type. 748 This is always set to `aiobungie.GroupType.CLAN` and should not be changed. 749 750 Returns 751 ------- 752 `collections.Sequence[aiobungie.crates.GroupMember]` 753 A sequence of joined groups for the fetched member. 754 """ 755 resp = await self.rest.fetch_groups_for_member( 756 member_id, member_type, filter=filter, group_type=group_type 757 ) 758 759 return tuple( 760 self.factory.deserialize_group_member(group) for group in resp["results"] 761 )
Fetch information about the groups that a given member has joined.
Parameters
- member_id (
int): The member's id - member_type (
aiobungie.MembershipType): The member's membership type.
Other Parameters
- filter (
int): Filter apply to list of joined groups. This Default to0 - group_type (
aiobungie.GroupType): The group's type. This is always set toaiobungie.GroupType.CLANand should not be changed.
Returns
collections.Sequence[aiobungie.crates.GroupMember]: A sequence of joined groups for the fetched member.
763 async def fetch_potential_groups_for_member( 764 self, 765 member_id: int, 766 member_type: enums.MembershipType | int, 767 /, 768 *, 769 filter: int = 0, 770 group_type: enums.GroupType | int = enums.GroupType.CLAN, 771 ) -> collections.Sequence[clans.GroupMember]: 772 """Fetch the potential groups for a clan member. 773 774 Parameters 775 ---------- 776 member_id : `int` 777 The member's id 778 member_type : `aiobungie.aiobungie.MembershipType | int` 779 The member's membership type. 780 781 Other Parameters 782 ---------------- 783 filter : `int` 784 Filter apply to list of joined groups. This Default to `0` 785 group_type : `aiobungie.aiobungie.GroupType | int` 786 The group's type. 787 This is always set to `aiobungie.GroupType.CLAN` and should not be changed. 788 789 Returns 790 ------- 791 `collections.Sequence[aiobungie.crates.GroupMember]` 792 A sequence of joined potential groups for the fetched member. 793 """ 794 resp = await self.rest.fetch_potential_groups_for_member( 795 member_id, member_type, filter=filter, group_type=group_type 796 ) 797 798 return tuple( 799 self.factory.deserialize_group_member(group) for group in resp["results"] 800 )
Fetch the potential groups for a clan member.
Parameters
- member_id (
int): The member's id - member_type (
aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
- filter (
int): Filter apply to list of joined groups. This Default to0 - group_type (
aiobungie.aiobungie.GroupType | int): The group's type. This is always set toaiobungie.GroupType.CLANand should not be changed.
Returns
collections.Sequence[aiobungie.crates.GroupMember]: A sequence of joined potential groups for the fetched member.
802 async def fetch_clan_members( 803 self, 804 clan_id: int, 805 /, 806 *, 807 name: str | None = None, 808 type: enums.MembershipType | int = enums.MembershipType.NONE, 809 ) -> iterators.Iterator[clans.ClanMember]: 810 """Fetch Bungie clan members. 811 812 Parameters 813 ---------- 814 clan_id : `int` 815 The clans id 816 817 Other Parameters 818 ---------------- 819 name : `str | None` 820 If provided, Only players matching this name will be returned. 821 type : `aiobungie.MembershipType` 822 An optional clan member's membership type. 823 This parameter is used to filter the returned results 824 by the provided membership, For an example XBox memberships only, 825 Otherwise will return all memberships. 826 827 Returns 828 ------- 829 `aiobungie.Iterator[aiobungie.crates.ClanMember]` 830 An iterator over the bungie clan members. 831 832 Raises 833 ------ 834 `aiobungie.NotFound` 835 The clan was not found. 836 """ 837 resp = await self.rest.fetch_clan_members(clan_id, type=type, name=name) 838 839 return self.factory.deserialize_clan_members(resp)
Fetch Bungie clan members.
Parameters
- clan_id (
int): The clans id
Other Parameters
- name (
str | None): If provided, Only players matching this name will be returned. - type (
aiobungie.MembershipType): An optional clan member's membership type. This parameter is used to filter the returned results by the provided membership, For an example XBox memberships only, Otherwise will return all memberships.
Returns
aiobungie.Iterator[aiobungie.crates.ClanMember]: An iterator over the bungie clan members.
Raises
aiobungie.NotFound: The clan was not found.
854 async def kick_clan_member( 855 self, 856 access_token: str, 857 /, 858 group_id: int, 859 membership_id: int, 860 membership_type: enums.MembershipType | int, 861 ) -> clans.Clan: 862 """Kick a member from the clan. 863 864 .. note:: 865 This request requires OAuth2: oauth2: `AdminGroups` scope. 866 867 Parameters 868 ---------- 869 access_token : `str` 870 The bearer access token associated with the bungie account. 871 group_id: `int` 872 The group id. 873 membership_id : `int` 874 The member id to kick. 875 membership_type : `aiobungie.aiobungie.MembershipType | int` 876 The member's membership type. 877 878 Returns 879 ------- 880 `aiobungie.crates.clan.Clan` 881 The clan that the member was kicked from. 882 """ 883 resp = await self.rest.kick_clan_member( 884 access_token, 885 group_id=group_id, 886 membership_id=membership_id, 887 membership_type=membership_type, 888 ) 889 890 return self.factory.deserialize_clan(resp)
Kick a member from the clan.
This request requires OAuth2: oauth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group id. - membership_id (
int): The member id to kick. - membership_type (
aiobungie.aiobungie.MembershipType | int): The member's membership type.
Returns
aiobungie.crates.clan.Clan: The clan that the member was kicked from.
892 async def fetch_clan_weekly_rewards(self, clan_id: int) -> milestones.Milestone: 893 """Fetch a Bungie clan's weekly reward state. 894 895 Parameters 896 ---------- 897 clan_id : `int` 898 The clan's id. 899 900 Returns 901 ------- 902 `aiobungie.crates.Milestone` 903 A runtime status of the clan's milestone data. 904 """ 905 906 resp = await self.rest.fetch_clan_weekly_rewards(clan_id) 907 908 return self.factory.deserialize_milestone(resp)
Fetch a Bungie clan's weekly reward state.
Parameters
- clan_id (
int): The clan's id.
Returns
aiobungie.crates.Milestone: A runtime status of the clan's milestone data.
912 async def fetch_inventory_item(self, hash: int, /) -> entity.InventoryEntity: 913 """Fetch a static inventory item entity given a its hash. 914 915 Parameters 916 ---------- 917 hash: `int` 918 Inventory item's hash. 919 920 Returns 921 ------- 922 `aiobungie.crates.InventoryEntity` 923 A bungie inventory item. 924 """ 925 resp = await self.rest.fetch_inventory_item(hash) 926 927 return self.factory.deserialize_inventory_entity(resp)
Fetch a static inventory item entity given a its hash.
Parameters
- hash (
int): Inventory item's hash.
Returns
aiobungie.crates.InventoryEntity: A bungie inventory item.
929 async def fetch_objective_entity(self, hash: int, /) -> entity.ObjectiveEntity: 930 """Fetch a Destiny objective entity given a its hash. 931 932 Parameters 933 ---------- 934 hash: `int` 935 objective's hash. 936 937 Returns 938 ------- 939 `aiobungie.crates.ObjectiveEntity` 940 An objective entity item. 941 """ 942 resp = await self.rest.fetch_objective_entity(hash) 943 944 return self.factory.deserialize_objective_entity(resp)
Fetch a Destiny objective entity given a its hash.
Parameters
- hash (
int): objective's hash.
Returns
aiobungie.crates.ObjectiveEntity: An objective entity item.
946 async def search_entities( 947 self, name: str, entity_type: str, *, page: int = 0 948 ) -> iterators.Iterator[entity.SearchableEntity]: 949 """Search for Destiny2 entities given a name and its type. 950 951 Parameters 952 ---------- 953 name : `str` 954 The name of the entity, i.e., Thunderlord, One thousand voices. 955 entity_type : `str` 956 The type of the entity, AKA Definition, 957 For an example `DestinyInventoryItemDefinition` for emblems, weapons, and other inventory items. 958 959 Other Parameters 960 ---------------- 961 page : `int` 962 An optional page to return. Default to 0. 963 964 Returns 965 ------- 966 `aiobungie.Iterator[aiobungie.crates.SearchableEntity]` 967 An iterator over the found results matching the provided name. 968 """ 969 resp = await self.rest.search_entities(name, entity_type, page=page) 970 971 return self.factory.deserialize_inventory_results(resp)
Search for Destiny2 entities given a name and its type.
Parameters
- name (
str): The name of the entity, i.e., Thunderlord, One thousand voices. - entity_type (
str): The type of the entity, AKA Definition, For an exampleDestinyInventoryItemDefinitionfor emblems, weapons, and other inventory items.
Other Parameters
- page (
int): An optional page to return. Default to 0.
Returns
aiobungie.Iterator[aiobungie.crates.SearchableEntity]: An iterator over the found results matching the provided name.
975 async def fetch_fireteams( 976 self, 977 activity_type: fireteams.FireteamActivity | int, 978 *, 979 platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY, 980 language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL, 981 date_range: int = 0, 982 page: int = 0, 983 slots_filter: int = 0, 984 ) -> collections.Sequence[fireteams.Fireteam]: 985 """Fetch public Bungie fireteams with open slots. 986 987 Parameters 988 ---------- 989 activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int` 990 The fireteam activity type. 991 992 Other Parameters 993 ---------------- 994 platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int` 995 If this is provided. Then the results will be filtered with the given platform. 996 Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms. 997 language : `aiobungie.crates.fireteams.FireteamLanguage | str` 998 A locale language to filter the used language in that fireteam. 999 Defaults to `aiobungie.crates.FireteamLanguage.ALL` 1000 date_range : `int` 1001 An integer to filter the date range of the returned fireteams. Defaults to `aiobungie.FireteamDate.ALL`. 1002 page : `int` 1003 The page number. By default its `0` which returns all available activities. 1004 slots_filter : `int` 1005 Filter the returned fireteams based on available slots. Default is `0` 1006 1007 Returns 1008 ------- 1009 `collections.Sequence[fireteams.Fireteam]` 1010 A sequence of `aiobungie.crates.Fireteam`. 1011 """ 1012 1013 resp = await self.rest.fetch_fireteams( 1014 activity_type, 1015 platform=platform, 1016 language=language, 1017 date_range=date_range, 1018 page=page, 1019 slots_filter=slots_filter, 1020 ) 1021 1022 return self.factory.deserialize_fireteams(resp)
Fetch public Bungie fireteams with open slots.
Parameters
- activity_type (
aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
- platform (
aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults toaiobungie.crates.FireteamPlatform.ANYwhich returns all platforms. - language (
FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults toaiobungie.crates.FireteamLanguage.ALL - date_range (
int): An integer to filter the date range of the returned fireteams. Defaults toaiobungie.FireteamDate.ALL. - page (
int): The page number. By default its0which returns all available activities. - slots_filter (
int): Filter the returned fireteams based on available slots. Default is0
Returns
collections.Sequence[fireteams.Fireteam]: A sequence ofaiobungie.crates.Fireteam.
1024 async def fetch_available_clan_fireteams( 1025 self, 1026 access_token: str, 1027 group_id: int, 1028 activity_type: fireteams.FireteamActivity | int, 1029 *, 1030 platform: fireteams.FireteamPlatform | int, 1031 language: fireteams.FireteamLanguage | str, 1032 date_range: int = 0, 1033 page: int = 0, 1034 public_only: bool = False, 1035 slots_filter: int = 0, 1036 ) -> collections.Sequence[fireteams.Fireteam]: 1037 """Fetch a clan's fireteams with open slots. 1038 1039 .. note:: 1040 This method requires OAuth2: ReadGroups scope. 1041 1042 Parameters 1043 ---------- 1044 access_token : `str` 1045 The bearer access token associated with the bungie account. 1046 group_id : `int` 1047 The group/clan id of the fireteam. 1048 activity_type : `aiobungie.aiobungie.crates.FireteamActivity | int` 1049 The fireteam activity type. 1050 1051 Other Parameters 1052 ---------------- 1053 platform : `aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int` 1054 If this is provided. Then the results will be filtered with the given platform. 1055 Defaults to `aiobungie.crates.FireteamPlatform.ANY` which returns all platforms. 1056 language : `aiobungie.crates.fireteams.FireteamLanguage | str` 1057 A locale language to filter the used language in that fireteam. 1058 Defaults to `aiobungie.crates.FireteamLanguage.ALL` 1059 date_range : `int` 1060 An integer to filter the date range of the returned fireteams. Defaults to `0`. 1061 page : `int` 1062 The page number. By default its `0` which returns all available activities. 1063 public_only: `bool` 1064 If set to True, Then only public fireteams will be returned. 1065 slots_filter : `int` 1066 Filter the returned fireteams based on available slots. Default is `0` 1067 1068 Returns 1069 ------- 1070 `collections.Sequence[aiobungie.crates.Fireteam]` 1071 A sequence of fireteams found in the clan. 1072 `None` will be returned if nothing was found. 1073 """ 1074 resp = await self.rest.fetch_available_clan_fireteams( 1075 access_token, 1076 group_id, 1077 activity_type, 1078 platform=platform, 1079 language=language, 1080 date_range=date_range, 1081 page=page, 1082 public_only=public_only, 1083 slots_filter=slots_filter, 1084 ) 1085 1086 return self.factory.deserialize_fireteams(resp)
Fetch a clan's fireteams with open slots.
This method requires OAuth2: ReadGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group/clan id of the fireteam. - activity_type (
aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
- platform (
aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults toaiobungie.crates.FireteamPlatform.ANYwhich returns all platforms. - language (
FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults toaiobungie.crates.FireteamLanguage.ALL - date_range (
int): An integer to filter the date range of the returned fireteams. Defaults to0. - page (
int): The page number. By default its0which returns all available activities. - public_only (
bool): If set to True, Then only public fireteams will be returned. - slots_filter (
int): Filter the returned fireteams based on available slots. Default is0
Returns
collections.Sequence[aiobungie.crates.Fireteam]: A sequence of fireteams found in the clan.Nonewill be returned if nothing was found.
1088 async def fetch_clan_fireteam( 1089 self, access_token: str, fireteam_id: int, group_id: int 1090 ) -> fireteams.AvailableFireteam: 1091 """Fetch a specific clan fireteam. 1092 1093 .. note:: 1094 This method requires OAuth2: ReadGroups scope. 1095 1096 Parameters 1097 ---------- 1098 access_token : `str` 1099 The bearer access token associated with the bungie account. 1100 group_id : `int` 1101 The group/clan id to fetch the fireteam from. 1102 fireteam_id : `int` 1103 The fireteam id to fetch. 1104 1105 Returns 1106 ------- 1107 `aiobungie.crates.AvailableFireteam` 1108 A sequence of available fireteams objects. 1109 """ 1110 resp = await self.rest.fetch_clan_fireteam(access_token, fireteam_id, group_id) 1111 1112 return self.factory.deserialize_available_fireteam(resp)
Fetch a specific clan fireteam.
This method requires OAuth2: ReadGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group/clan id to fetch the fireteam from. - fireteam_id (
int): The fireteam id to fetch.
Returns
aiobungie.crates.AvailableFireteam: A sequence of available fireteams objects.
1114 async def fetch_my_clan_fireteams( 1115 self, 1116 access_token: str, 1117 group_id: int, 1118 *, 1119 include_closed: bool = True, 1120 platform: fireteams.FireteamPlatform | int, 1121 language: fireteams.FireteamLanguage | str, 1122 filtered: bool = True, 1123 page: int = 0, 1124 ) -> collections.Sequence[fireteams.AvailableFireteam]: 1125 """A method that's similar to `fetch_fireteams` but requires OAuth2. 1126 1127 .. note:: 1128 This method requires OAuth2: ReadGroups scope. 1129 1130 Parameters 1131 ---------- 1132 access_token : str 1133 The bearer access token associated with the bungie account. 1134 group_id : int 1135 The group/clan id to fetch. 1136 1137 Other Parameters 1138 ---------------- 1139 include_closed : `bool` 1140 If provided and set to True, It will also return closed fireteams. 1141 If provided and set to False, It will only return public fireteams. Default is True. 1142 platform : aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int 1143 If this is provided. Then the results will be filtered with the given platform. 1144 Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms. 1145 language : `aiobungie.crates.fireteams.FireteamLanguage | str` 1146 A locale language to filter the used language in that fireteam. 1147 Defaults to aiobungie.crates.FireteamLanguage.ALL 1148 filtered : `bool` 1149 If set to True, it will filter by clan. Otherwise not. Default is True. 1150 page : `int` 1151 The page number. By default its 0 which returns all available activities. 1152 1153 Returns 1154 ------- 1155 `collections.Sequence[aiobungie.crates.AvailableFireteam]` 1156 A sequence of available fireteams objects if exists. else `None` will be returned. 1157 """ 1158 resp = await self.rest.fetch_my_clan_fireteams( 1159 access_token, 1160 group_id, 1161 include_closed=include_closed, 1162 platform=platform, 1163 language=language, 1164 filtered=filtered, 1165 page=page, 1166 ) 1167 1168 return self.factory.deserialize_available_fireteams(resp)
A method that's similar to fetch_fireteams but requires OAuth2.
This method requires OAuth2: ReadGroups scope.
Parameters
- access_token (str): The bearer access token associated with the bungie account.
- group_id (int): The group/clan id to fetch.
Other Parameters
- include_closed (
bool): If provided and set to True, It will also return closed fireteams. If provided and set to False, It will only return public fireteams. Default is True. - platform (aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults to aiobungie.crates.FireteamPlatform.ANY which returns all platforms.
- language (
FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults to aiobungie.crates.FireteamLanguage.ALL - filtered (
bool): If set to True, it will filter by clan. Otherwise not. Default is True. - page (
int): The page number. By default its 0 which returns all available activities.
Returns
collections.Sequence[aiobungie.crates.AvailableFireteam]: A sequence of available fireteams objects if exists. elseNonewill be returned.
1172 async def fetch_friends( 1173 self, access_token: str, / 1174 ) -> collections.Sequence[friends.Friend]: 1175 """Fetch bungie friend list. 1176 1177 .. note:: 1178 This requests OAuth2: ReadUserData scope. 1179 1180 Parameters 1181 ----------- 1182 access_token : `str` 1183 The bearer access token associated with the bungie account. 1184 1185 Returns 1186 ------- 1187 `collections.Sequence[aiobungie.crates.Friend]` 1188 A sequence of the friends associated with that access token. 1189 """ 1190 1191 resp = await self.rest.fetch_friends(access_token) 1192 1193 return self.factory.deserialize_friends(resp)
Fetch bungie friend list.
This requests OAuth2: ReadUserData scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account.
Returns
collections.Sequence[aiobungie.crates.Friend]: A sequence of the friends associated with that access token.
1195 async def fetch_friend_requests( 1196 self, access_token: str, / 1197 ) -> friends.FriendRequestView: 1198 """Fetch pending bungie friend requests queue. 1199 1200 .. note:: 1201 This requests OAuth2: ReadUserData scope. 1202 1203 Parameters 1204 ----------- 1205 access_token : `str` 1206 The bearer access token associated with the bungie account. 1207 1208 Returns 1209 ------- 1210 `aiobungie.crates.FriendRequestView` 1211 A friend requests view of that associated access token. 1212 """ 1213 1214 resp = await self.rest.fetch_friend_requests(access_token) 1215 1216 return self.factory.deserialize_friend_requests(resp)
Fetch pending bungie friend requests queue.
This requests OAuth2: ReadUserData scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account.
Returns
aiobungie.crates.FriendRequestView: A friend requests view of that associated access token.
1220 async def fetch_application(self, appid: int, /) -> application.Application: 1221 """Fetch a Bungie application. 1222 1223 Parameters 1224 ----------- 1225 appid: `int` 1226 The application id. 1227 1228 Returns 1229 -------- 1230 `aiobungie.crates.Application` 1231 A Bungie application. 1232 """ 1233 resp = await self.rest.fetch_application(appid) 1234 1235 return self.factory.deserialize_app(resp)
Fetch a Bungie application.
Parameters
- appid (
int): The application id.
Returns
aiobungie.crates.Application: A Bungie application.
1239 async def fetch_public_milestone_content( 1240 self, milestone_hash: int, / 1241 ) -> milestones.MilestoneContent: 1242 """Fetch the milestone content given its hash. 1243 1244 Parameters 1245 ---------- 1246 milestone_hash : `int` 1247 The milestone hash. 1248 1249 Returns 1250 ------- 1251 `aiobungie.crates.milestones.MilestoneContent` 1252 A milestone content object. 1253 """ 1254 ... 1255 resp = await self.rest.fetch_public_milestone_content(milestone_hash) 1256 return self.factory.deserialize_public_milestone_content(resp)
Fetch the milestone content given its hash.
Parameters
- milestone_hash (
int): The milestone hash.
Returns
aiobungie.crates.milestones.MilestoneContent: A milestone content object.
771@typing.final 772class ClosedReasons(Flag): 773 """A Flags enumeration representing the reasons why a person can't join this user's fireteam.""" 774 775 NONE = 0 776 MATCHMAKING = 1 << 0 777 LOADING = 1 << 1 778 SOLO = 1 << 2 779 """The activity is required to be played solo.""" 780 INTERNAL_REASONS = 1 << 3 781 """ 782 The user can't be joined for one of a variety of internal reasons. 783 Basically, the game can't let you join at this time, 784 but for reasons that aren't under the control of this user 785 """ 786 DISALLOWED_BY_GAME_STATE = 1 << 4 787 """The user's current activity/quest/other transitory game state is preventing joining.""" 788 OFFLINE = 32768 789 """The user appears offline."""
A Flags enumeration representing the reasons why a person can't join this user's fireteam.
The user can't be joined for one of a variety of internal reasons. Basically, the game can't let you join at this time, but for reasons that aren't under the control of this user
The user's current activity/quest/other transitory game state is preventing joining.
74@typing.final 75class ComponentFields(enums.Enum): 76 """An enum that provides fields found in a base component response.""" 77 78 PRIVACY = ComponentPrivacy.NONE 79 DISABLED = False
An enum that provides fields found in a base component response.
65@typing.final 66class ComponentPrivacy(int, enums.Enum): 67 """An enum the provides privacy settings for profile components.""" 68 69 NONE = 0 70 PUBLIC = 1 71 PRIVATE = 2
An enum the provides privacy settings for profile components.
347@typing.final 348class ComponentType(Enum): 349 """An Enum for Destiny 2 profile Components.""" 350 351 NONE = 0 352 353 PROFILE = 100 354 PROFILE_INVENTORIES = 102 355 PROFILE_CURRENCIES = 103 356 PROFILE_PROGRESSION = 104 357 ALL_PROFILES = ( 358 PROFILE, 359 PROFILE_INVENTORIES, 360 PROFILE_CURRENCIES, 361 PROFILE_PROGRESSION, 362 ) 363 """All profile components.""" 364 365 VENDORS = 400 366 VENDOR_SALES = 402 367 VENDOR_RECEIPTS = 101 368 ALL_VENDORS = (VENDORS, VENDOR_RECEIPTS, VENDOR_SALES) 369 """All vendor components.""" 370 371 # Items 372 ITEM_INSTANCES = 300 373 ITEM_OBJECTIVES = 301 374 ITEM_PERKS = 302 375 ITEM_RENDER_DATA = 303 376 ITEM_STATS = 304 377 ITEM_SOCKETS = 305 378 ITEM_TALENT_GRINDS = 306 379 ITEM_PLUG_STATES = 308 380 ITEM_PLUG_OBJECTIVES = 309 381 ITEM_REUSABLE_PLUGS = 310 382 383 ALL_ITEMS = ( 384 ITEM_PLUG_OBJECTIVES, 385 ITEM_PLUG_STATES, 386 ITEM_SOCKETS, 387 ITEM_INSTANCES, 388 ITEM_OBJECTIVES, 389 ITEM_PERKS, 390 ITEM_RENDER_DATA, 391 ITEM_STATS, 392 ITEM_TALENT_GRINDS, 393 ITEM_REUSABLE_PLUGS, 394 ) 395 """All item components.""" 396 397 PLATFORM_SILVER = 105 398 KIOSKS = 500 399 CURRENCY_LOOKUPS = 600 400 PRESENTATION_NODES = 700 401 COLLECTIBLES = 800 402 RECORDS = 900 403 TRANSITORY = 1000 404 METRICS = 1100 405 INVENTORIES = 102 406 STRING_VARIABLES = 1200 407 CRAFTABLES = 1300 408 409 CHARACTERS = 200 410 CHARACTER_INVENTORY = 201 411 CHARECTER_PROGRESSION = 202 412 CHARACTER_RENDER_DATA = 203 413 CHARACTER_ACTIVITIES = 204 414 CHARACTER_EQUIPMENT = 205 415 CHARACTER_LOADOUTS = 206 416 417 ALL_CHARACTERS = ( 418 CHARACTERS, 419 CHARACTER_INVENTORY, 420 CHARECTER_PROGRESSION, 421 CHARACTER_RENDER_DATA, 422 CHARACTER_ACTIVITIES, 423 CHARACTER_EQUIPMENT, 424 CHARACTER_LOADOUTS, 425 RECORDS, 426 ) 427 """All character components.""" 428 429 # Ignores: We those are iterables, They're tuples. 430 ALL = ( 431 *ALL_PROFILES, # pyright: ignore[reportGeneralTypeIssues] 432 *ALL_CHARACTERS, # pyright: ignore[reportGeneralTypeIssues] 433 *ALL_VENDORS, # pyright: ignore[reportGeneralTypeIssues] 434 *ALL_ITEMS, # pyright: ignore[reportGeneralTypeIssues] 435 RECORDS, 436 CURRENCY_LOOKUPS, 437 PRESENTATION_NODES, 438 COLLECTIBLES, 439 KIOSKS, 440 METRICS, 441 PLATFORM_SILVER, 442 INVENTORIES, 443 STRING_VARIABLES, 444 TRANSITORY, 445 CRAFTABLES, 446 ) 447 """ALl components included."""
An Enum for Destiny 2 profile Components.
All item components.
All character components.
ALl components included.
653@typing.final 654class CredentialType(int, Enum): 655 """The types of the accounts system supports at bungie.""" 656 657 NONE = 0 658 XUID = 1 659 PSNID = 2 660 WILD = 3 661 FAKE = 4 662 FACEBOOK = 5 663 GOOGLE = 8 664 WINDOWS = 9 665 DEMONID = 10 666 STEAMID = 12 667 BATTLENETID = 14 668 STADIAID = 16 669 TWITCHID = 18
The types of the accounts system supports at bungie.
531@typing.final 532class DamageType(int, Enum): 533 """Enums for Destiny Damage types""" 534 535 NONE = 0 536 KINETIC = 1 537 ARC = 2 538 SOLAR = 3 539 VOID = 4 540 RAID = 5 541 """This is a special damage type reserved for some raid activity encounters.""" 542 STASIS = 6
Enums for Destiny Damage types
This is a special damage type reserved for some raid activity encounters.
63@typing.final 64class Difficulty(int, enums.Enum): 65 """An enum for activities difficulties.""" 66 67 TRIVIAL = 0 68 EASY = 1 69 NORMAL = 2 70 CHALLENGING = 3 71 HARD = 4 72 BRAVE = 5 73 ALMOST_IMPOSSIBLE = 6 74 IMPOSSIBLE = 7
An enum for activities difficulties.
149@typing.final 150class Dungeon(int, Enum): 151 """An Enum for all available Dungeon/Like missions in Destiny 2.""" 152 153 NORMAL_PRESAGE = 2124066889 154 """Normal Presage""" 155 156 MASTER_PRESAGE = 4212753278 157 """Master Presage""" 158 159 HARBINGER = 1738383283 160 """Harbinger""" 161 162 PROPHECY = 4148187374 163 """Prophecy""" 164 165 MASTER_POH = 785700673 166 """Master Pit of Heresy?""" 167 168 LEGEND_POH = 785700678 169 """Legend Pit of Heresy?""" 170 171 POH = 1375089621 172 """Normal Pit of Heresy.""" 173 174 SHATTERED = 2032534090 175 """Shattered Throne""" 176 177 GOA_LEGEND = 4078656646 178 """Grasp of Avarice legend.""" 179 180 GOA_MASTER = 3774021532 181 """Grasp of Avarice master."""
An Enum for all available Dungeon/Like missions in Destiny 2.
2420class EmptyFactory(Factory): 2421 """A stand-alone factory that doesn't require a client instance. 2422 2423 # Example 2424 --------- 2425 ```py 2426 # We'll implement a serializable RESTClient. 2427 @dataclass(slots=True) 2428 class MyClient(aiobungie.traits.Serializable): 2429 rest = aiobungie.RESTClient(env["CLIENT_TOKEN"]) 2430 my_name = "Fate怒" 2431 my_code = 4275 2432 2433 # Must implement this one method. 2434 @property 2435 def factory(self) -> aiobungie.EmptyFactory: 2436 # Return an empty factory 2437 return aiobungie.EmptyFactory() 2438 2439 async def my_memberships(self) -> Sequence[aiobungie.crates.DestinyMembership]: 2440 # Note, Do not call methods within objects, Since this is an empty 2441 # factory, The client reference that makes these calls will be `None`. 2442 response = await self.rest.fetch_membership(self.my_name, self.my_code) 2443 return self.factory.deserialize_destiny_memberships(response) 2444 2445 2446 async def main() -> None: 2447 client = MyClient() 2448 async with client.client: 2449 print(await client.my_memberships()) 2450 2451 asyncio.run(main()) 2452 ``` 2453 """ 2454 2455 __slots__ = () 2456 2457 if typing.TYPE_CHECKING: 2458 # We explicitly want this to be `None`. 2459 _net: None 2460 2461 def __init__(self, net: None = None) -> None: 2462 self._net = net
A stand-alone factory that doesn't require a client instance.
# Example
# We'll implement a serializable RESTClient.
@dataclass(slots=True)
class MyClient(aiobungie.traits.Serializable):
rest = aiobungie.RESTClient(env["CLIENT_TOKEN"])
my_name = "Fate怒"
my_code = 4275
# Must implement this one method.
@property
def factory(self) -> aiobungie.EmptyFactory:
# Return an empty factory
return aiobungie.EmptyFactory()
async def my_memberships(self) -> Sequence[aiobungie.crates.DestinyMembership]:
# Note, Do not call methods within objects, Since this is an empty
# factory, The client reference that makes these calls will be `None`.
response = await self.rest.fetch_membership(self.my_name, self.my_code)
return self.factory.deserialize_destiny_memberships(response)
async def main() -> None:
client = MyClient()
async with client.client:
print(await client.my_memberships())
asyncio.run(main())
Inherited Members
- Factory
- deserialize_bungie_user
- deserialize_partial_bungie_user
- deserialize_destiny_membership
- deserialize_destiny_memberships
- deserialize_user
- deserialize_searched_user
- deserialize_user_credentials
- deserialize_user_themes
- deserialize_clan
- deserialize_clan_member
- deserialize_clan_members
- deserialize_group_member
- deserialize_clan_conversations
- deserialize_app_owner
- deserialize_app
- deserialize_profile
- deserialize_profile_item
- deserialize_objectives
- deserialize_records
- deserialize_character_records
- deserialize_character_dye
- deserialize_character_customization
- deserialize_character_minimal_equipments
- deserialize_character_render_data
- deserialize_available_activity
- deserialize_character_activity
- deserialize_profile_items
- deserialize_progressions
- deserialize_milestone
- deserialize_characters
- deserialize_character
- deserialize_character_equipments
- deserialize_character_activities
- deserialize_characters_render_data
- deserialize_character_progressions
- deserialize_character_progressions_mapping
- deserialize_characters_records
- deserialize_profile_records
- deserialize_craftables_component
- deserialize_components
- deserialize_items_component
- deserialize_character_component
- deserialize_inventory_results
- deserialize_inventory_entity
- deserialize_objective_entity
- deserialize_activity
- deserialize_activities
- deserialize_extended_weapon_values
- deserialize_post_activity_player
- deserialize_post_activity
- deserialize_aggregated_activity
- deserialize_aggregated_activities
- deserialize_linked_profiles
- deserialize_public_milestone_content
- deserialize_friend
- deserialize_friends
- deserialize_friend_requests
- deserialize_fireteams
- deserialize_fireteam_destiny_users
- deserialize_fireteam_members
- deserialize_available_fireteam
- deserialize_available_fireteams
- deserialize_fireteam_party
- deserialize_seasonal_artifact
- deserialize_profile_progression
- deserialize_instanced_item
- deserialize_item_energy
- deserialize_item_perk
- deserialize_item_socket
- deserialize_item_stats_view
- deserialize_plug_item_state
69class Enum(__enum.Enum): 70 """Builtin Python enum with extra handlings.""" 71 72 @property 73 def name(self) -> str: 74 return self._name_ 75 76 @property 77 def value(self) -> typing.Any: 78 return self._value_ 79 80 def __str__(self) -> str: 81 return self._name_ 82 83 def __repr__(self) -> str: 84 return f"<{type(self).__name__}.{self._name_}: {self._value_!s}>" 85 86 def __int__(self) -> int: 87 return int(self.value)
Builtin Python enum with extra handlings.
60class Factory(interfaces.FactoryInterface): 61 """The base deserialization factory class for all aiobungie objects. 62 63 This entity factory is used to deserialize JSON responses from the REST client and turning them 64 into a `aiobungie.crates` Python classes. 65 """ 66 67 __slots__ = ("_net",) 68 69 def __init__(self, net: traits.Netrunner) -> None: 70 self._net = net 71 72 def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser: 73 return user.BungieUser( 74 id=int(data["membershipId"]), 75 created_at=time.clean_date(data["firstAccess"]), 76 name=data.get("cachedBungieGlobalDisplayName"), 77 is_deleted=data["isDeleted"], 78 about=data["about"], 79 updated_at=time.clean_date(data["lastUpdate"]), 80 psn_name=data.get("psnDisplayName", None), 81 stadia_name=data.get("stadiaDisplayName", None), 82 steam_name=data.get("steamDisplayName", None), 83 twitch_name=data.get("twitchDisplayName", None), 84 blizzard_name=data.get("blizzardDisplayName", None), 85 status=data["statusText"], 86 locale=data["locale"], 87 picture=assets.Image(path=data["profilePicturePath"]), 88 code=data.get("cachedBungieGlobalDisplayNameCode", None), 89 unique_name=data.get("uniqueName", None), 90 theme_id=int(data["profileTheme"]), 91 show_activity=bool(data["showActivity"]), 92 theme_name=data["profileThemeName"], 93 display_title=data["userTitleDisplay"], 94 ) 95 96 def deserialize_partial_bungie_user( 97 self, payload: typedefs.JSONObject 98 ) -> user.PartialBungieUser: 99 return user.PartialBungieUser( 100 net=self._net, 101 types=tuple( 102 enums.MembershipType(type_) 103 for type_ in payload.get("applicableMembershipTypes", ()) 104 ), 105 name=payload.get("displayName"), 106 id=int(payload["membershipId"]), 107 crossave_override=enums.MembershipType(payload["crossSaveOverride"]), 108 is_public=payload["isPublic"], 109 icon=assets.Image(path=payload.get("iconPath", "")), 110 type=enums.MembershipType(payload["membershipType"]), 111 ) 112 113 def deserialize_destiny_membership( 114 self, payload: typedefs.JSONObject 115 ) -> user.DestinyMembership: 116 name: str | None = None 117 if (raw_name := payload.get("bungieGlobalDisplayName")) is not None: 118 name = typedefs.unknown(raw_name) 119 120 return user.DestinyMembership( 121 net=self._net, 122 id=int(payload["membershipId"]), 123 name=name, 124 code=payload.get("bungieGlobalDisplayNameCode", None), 125 last_seen_name=payload.get("LastSeenDisplayName") 126 or payload.get("displayName") # noqa: W503 127 or "", # noqa: W503 128 type=enums.MembershipType(payload["membershipType"]), 129 is_public=payload["isPublic"], 130 crossave_override=enums.MembershipType(payload["crossSaveOverride"]), 131 icon=assets.Image(path=payload.get("iconPath", "")), 132 types=tuple( 133 enums.MembershipType(type_) 134 for type_ in payload.get("applicableMembershipTypes", ()) 135 ), 136 ) 137 138 def deserialize_destiny_memberships( 139 self, data: typedefs.JSONArray 140 ) -> collections.Sequence[user.DestinyMembership]: 141 return tuple( 142 self.deserialize_destiny_membership(membership) for membership in data 143 ) 144 145 def deserialize_user(self, data: typedefs.JSONObject) -> user.User: 146 primary_membership_id: int | None = None 147 if raw_primary_id := data.get("primaryMembershipId"): 148 primary_membership_id = int(raw_primary_id) 149 150 return user.User( 151 bungie_user=self.deserialize_bungie_user(data["bungieNetUser"]), 152 memberships=self.deserialize_destiny_memberships( 153 data["destinyMemberships"] 154 ), 155 primary_membership_id=primary_membership_id, 156 ) 157 158 def deserialize_searched_user( 159 self, payload: typedefs.JSONObject 160 ) -> user.SearchableDestinyUser: 161 code: int | None = None 162 if raw_code := payload.get("bungieGlobalDisplayNameCode"): 163 code = int(raw_code) 164 165 bungie_id: int | None = None 166 if raw_bungie_id := payload.get("bungieNetMembershipId"): 167 bungie_id = int(raw_bungie_id) 168 169 return user.SearchableDestinyUser( 170 name=typedefs.unknown(payload["bungieGlobalDisplayName"]), 171 code=code, 172 bungie_id=bungie_id, 173 memberships=self.deserialize_destiny_memberships( 174 payload["destinyMemberships"] 175 ), 176 ) 177 178 def deserialize_user_credentials( 179 self, payload: typedefs.JSONArray 180 ) -> collections.Sequence[user.UserCredentials]: 181 return tuple( 182 user.UserCredentials( 183 type=enums.CredentialType(int(creds["credentialType"])), 184 display_name=creds["credentialDisplayName"], 185 is_public=creds["isPublic"], 186 self_as_string=creds.get("credentialAsString"), 187 ) 188 for creds in payload 189 ) 190 191 def deserialize_user_themes( 192 self, payload: typedefs.JSONArray 193 ) -> collections.Sequence[user.UserThemes]: 194 return tuple( 195 user.UserThemes( 196 id=int(entry["userThemeId"]), 197 name=entry["userThemeName"] if "userThemeName" in entry else None, 198 description=entry["userThemeDescription"] 199 if "userThemeDescription" in entry 200 else None, 201 ) 202 for entry in payload 203 ) 204 205 def _deserialize_group_details( 206 self, 207 data: typedefs.JSONObject, 208 current_user_memberships: collections.Mapping[str, clans.ClanMember] 209 | None = None, 210 clan_founder: clans.ClanMember | None = None, 211 ) -> clans.Clan: 212 features = data["features"] 213 features_obj = clans.ClanFeatures( 214 max_members=features["maximumMembers"], 215 max_membership_types=features["maximumMembershipsOfGroupType"], 216 capabilities=features["capabilities"], 217 membership_types=features["membershipTypes"], 218 invite_permissions=features["invitePermissionOverride"], 219 update_banner_permissions=features["updateBannerPermissionOverride"], 220 update_culture_permissions=features["updateCulturePermissionOverride"], 221 join_level=features["joinLevel"], 222 ) 223 information: typedefs.JSONObject = data["clanInfo"] 224 progression: collections.Mapping[int, progressions.Progression] = { 225 int(prog_hash): self.deserialize_progressions(prog) 226 for prog_hash, prog in information["d2ClanProgressions"].items() 227 } 228 229 return clans.Clan( 230 net=self._net, 231 id=int(data["groupId"]), 232 name=data["name"], 233 type=enums.GroupType(data["groupType"]), 234 created_at=time.clean_date(data["creationDate"]), 235 member_count=data["memberCount"], 236 motto=data["motto"], 237 about=data["about"], 238 is_public=data["isPublic"], 239 banner=assets.Image(path=data["bannerPath"]), 240 avatar=assets.Image(path=data["avatarPath"]), 241 tags=tuple(data["tags"]), 242 features=features_obj, 243 owner=clan_founder, 244 progressions=progression, 245 call_sign=information["clanCallsign"], 246 banner_data=information["clanBannerData"], 247 chat_security=data["chatSecurity"], 248 conversation_id=int(data["conversationId"]), 249 allow_chat=data["allowChat"], 250 theme=data["theme"], 251 current_user_membership=current_user_memberships, 252 ) 253 254 def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan: 255 current_user_map: collections.Mapping[str, clans.ClanMember] | None = None 256 if raw_current_user := payload.get("currentUserMemberMap"): 257 # This will get populated if only it was a GroupsV2.GroupResponse. 258 # GroupsV2.GetGroupsForMemberResponse doesn't have this field. 259 current_user_map = { 260 membership_type: self.deserialize_clan_member(membership) 261 for membership_type, membership in raw_current_user.items() 262 } 263 264 return self._deserialize_group_details( 265 data=payload["detail"], 266 clan_founder=self.deserialize_clan_member(payload["founder"]), 267 current_user_memberships=current_user_map, 268 ) 269 270 def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember: 271 destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"]) 272 return clans.ClanMember( 273 net=self._net, 274 last_seen_name=destiny_user.last_seen_name, 275 id=destiny_user.id, 276 name=destiny_user.name, 277 icon=destiny_user.icon, 278 last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])), 279 group_id=int(data["groupId"]), 280 joined_at=time.clean_date(data["joinDate"]), 281 types=destiny_user.types, 282 is_public=destiny_user.is_public, 283 type=destiny_user.type, 284 code=destiny_user.code, 285 is_online=data["isOnline"], 286 crossave_override=destiny_user.crossave_override, 287 bungie_user=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"]) 288 if "bungieNetUserInfo" in data 289 else None, 290 member_type=enums.ClanMemberType(int(data["memberType"])), 291 ) 292 293 def deserialize_clan_members( 294 self, data: typedefs.JSONObject, / 295 ) -> iterators.Iterator[clans.ClanMember]: 296 return iterators.Iterator( 297 self.deserialize_clan_member(member) for member in data["results"] 298 ) 299 300 def deserialize_group_member( 301 self, payload: typedefs.JSONObject 302 ) -> clans.GroupMember: 303 member = payload["member"] 304 return clans.GroupMember( 305 net=self._net, 306 join_date=time.clean_date(member["joinDate"]), 307 group_id=int(member["groupId"]), 308 member_type=enums.ClanMemberType(member["memberType"]), 309 is_online=member["isOnline"], 310 last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])), 311 inactive_memberships=payload.get("areAllMembershipsInactive", None), 312 member=self.deserialize_destiny_membership(member["destinyUserInfo"]), 313 group=self._deserialize_group_details(payload["group"]), 314 ) 315 316 def _deserialize_clan_conversation( 317 self, payload: typedefs.JSONObject 318 ) -> clans.ClanConversation: 319 return clans.ClanConversation( 320 net=self._net, 321 id=int(payload["conversationId"]), 322 group_id=int(payload["groupId"]), 323 name=typedefs.unknown(payload["chatName"]), 324 chat_enabled=payload["chatEnabled"], 325 security=payload["chatSecurity"], 326 ) 327 328 def deserialize_clan_conversations( 329 self, payload: typedefs.JSONArray 330 ) -> collections.Sequence[clans.ClanConversation]: 331 return tuple(self._deserialize_clan_conversation(conv) for conv in payload) 332 333 def deserialize_app_owner( 334 self, payload: typedefs.JSONObject 335 ) -> application.ApplicationOwner: 336 return application.ApplicationOwner( 337 net=self._net, 338 name=payload.get("bungieGlobalDisplayName"), 339 id=int(payload["membershipId"]), 340 type=enums.MembershipType(payload["membershipType"]), 341 icon=assets.Image(path=payload["iconPath"]), 342 is_public=payload["isPublic"], 343 code=payload.get("bungieGlobalDisplayNameCode", None), 344 ) 345 346 def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application: 347 return application.Application( 348 id=int(payload["applicationId"]), 349 name=payload["name"], 350 link=payload["link"], 351 status=payload["status"], 352 redirect_url=payload.get("redirectUrl", None), 353 created_at=time.clean_date(payload["creationDate"]), 354 published_at=time.clean_date(payload["firstPublished"]), 355 owner=self.deserialize_app_owner(payload["team"][0]["user"]), 356 scope=payload.get("scope"), 357 ) 358 359 def _set_character_attrs(self, payload: typedefs.JSONObject) -> character.Character: 360 return character.Character( 361 net=self._net, 362 id=int(payload["characterId"]), 363 gender=enums.Gender(payload["genderType"]), 364 race=enums.Race(payload["raceType"]), 365 class_type=enums.Class(payload["classType"]), 366 emblem=assets.Image(path=payload["emblemBackgroundPath"]) 367 if "emblemBackgroundPath" in payload 368 else None, 369 emblem_icon=assets.Image(path=payload["emblemPath"]) 370 if "emblemPath" in payload 371 else None, 372 emblem_hash=int(payload["emblemHash"]) if "emblemHash" in payload else None, 373 last_played=time.clean_date(payload["dateLastPlayed"]), 374 total_played_time=int(payload["minutesPlayedTotal"]), 375 member_id=int(payload["membershipId"]), 376 member_type=enums.MembershipType(payload["membershipType"]), 377 level=payload["baseCharacterLevel"], 378 title_hash=payload.get("titleRecordHash", None), 379 light=payload["light"], 380 stats={enums.Stat(int(k)): v for k, v in payload["stats"].items()}, 381 ) 382 383 def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile: 384 payload = payload["data"] 385 id = int(payload["userInfo"]["membershipId"]) 386 name = payload["userInfo"]["displayName"] 387 is_public = payload["userInfo"]["isPublic"] 388 type = enums.MembershipType(payload["userInfo"]["membershipType"]) 389 last_played = time.clean_date(payload["dateLastPlayed"]) 390 character_ids = tuple(int(cid) for cid in payload["characterIds"]) 391 power_cap = payload["currentSeasonRewardPowerCap"] 392 393 return profile.Profile( 394 id=int(id), 395 name=name, 396 is_public=is_public, 397 type=type, 398 last_played=last_played, 399 character_ids=character_ids, 400 power_cap=power_cap, 401 net=self._net, 402 ) 403 404 def deserialize_profile_item( 405 self, payload: typedefs.JSONObject 406 ) -> profile.ProfileItemImpl: 407 instance_id: int | None = None 408 if raw_instance_id := payload.get("itemInstanceId"): 409 instance_id = int(raw_instance_id) 410 411 version_number: int | None = None 412 if raw_version := payload.get("versionNumber"): 413 version_number = int(raw_version) 414 415 transfer_status = enums.TransferStatus(payload["transferStatus"]) 416 417 return profile.ProfileItemImpl( 418 net=self._net, 419 hash=payload["itemHash"], 420 quantity=payload["quantity"], 421 bind_status=enums.ItemBindStatus(payload["bindStatus"]), 422 location=enums.ItemLocation(payload["location"]), 423 bucket=payload["bucketHash"], 424 transfer_status=transfer_status, 425 lockable=payload["lockable"], 426 state=enums.ItemState(payload["state"]), 427 dismantle_permissions=payload["dismantlePermission"], 428 is_wrapper=payload["isWrapper"], 429 instance_id=instance_id, 430 version_number=version_number, 431 ornament_id=payload.get("overrideStyleItemHash"), 432 ) 433 434 def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective: 435 return records.Objective( 436 net=self._net, 437 hash=payload["objectiveHash"], 438 visible=payload["visible"], 439 complete=payload["complete"], 440 completion_value=payload["completionValue"], 441 progress=payload.get("progress"), 442 destination_hash=payload.get("destinationHash"), 443 activity_hash=payload.get("activityHash"), 444 ) 445 446 # TODO: Remove **nodes and get it directly from the payload. 447 def deserialize_records( 448 self, 449 payload: typedefs.JSONObject, 450 scores: records.RecordScores | None = None, 451 **nodes: int, 452 ) -> records.Record: 453 objectives: collections.Sequence[records.Objective] | None = None 454 interval_objectives: collections.Sequence[records.Objective] | None = None 455 record_state: records.RecordState | int 456 457 record_state = records.RecordState(payload["state"]) 458 459 if raw_objs := payload.get("objectives"): 460 objectives = tuple(self.deserialize_objectives(obj) for obj in raw_objs) 461 462 if raw_interval_objs := payload.get("intervalObjectives"): 463 interval_objectives = tuple( 464 self.deserialize_objectives(obj) for obj in raw_interval_objs 465 ) 466 467 return records.Record( 468 scores=scores, 469 categories_node_hash=nodes.get("categories_hash"), 470 seals_node_hash=nodes.get("seals_hash"), 471 state=record_state, 472 objectives=objectives, 473 interval_objectives=interval_objectives, 474 redeemed_count=payload.get("intervalsRedeemedCount", 0), 475 completion_times=payload.get("completedCount", None), 476 reward_visibility=payload.get("rewardVisibility"), 477 ) 478 479 def deserialize_character_records( 480 self, 481 payload: typedefs.JSONObject, 482 scores: records.RecordScores | None = None, 483 record_hashes: collections.Sequence[int] = (), 484 ) -> records.CharacterRecord: 485 record = self.deserialize_records(payload, scores) 486 return records.CharacterRecord( 487 scores=scores, 488 categories_node_hash=record.categories_node_hash, 489 seals_node_hash=record.seals_node_hash, 490 state=record.state, 491 objectives=record.objectives, 492 interval_objectives=record.interval_objectives, 493 redeemed_count=payload.get("intervalsRedeemedCount", 0), 494 completion_times=payload.get("completedCount"), 495 reward_visibility=payload.get("rewardVisibility"), 496 record_hashes=record_hashes, 497 ) 498 499 def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye: 500 return character.Dye( 501 channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"] 502 ) 503 504 def deserialize_character_customization( 505 self, payload: typedefs.JSONObject 506 ) -> character.CustomizationOptions: 507 return character.CustomizationOptions( 508 personality=payload["personality"], 509 face=payload["face"], 510 skin_color=payload["skinColor"], 511 lip_color=payload["lipColor"], 512 eye_color=payload["eyeColor"], 513 hair_colors=payload.get("hairColors", ()), 514 feature_colors=payload.get("featureColors", ()), 515 decal_color=payload["decalColor"], 516 wear_helmet=payload["wearHelmet"], 517 hair_index=payload["hairIndex"], 518 feature_index=payload["featureIndex"], 519 decal_index=payload["decalIndex"], 520 ) 521 522 def deserialize_character_minimal_equipments( 523 self, payload: typedefs.JSONObject 524 ) -> character.MinimalEquipments: 525 if raw_dyes := payload.get("dyes"): 526 dyes = tuple(self.deserialize_character_dye(dye) for dye in raw_dyes) 527 else: 528 dyes = () 529 530 return character.MinimalEquipments( 531 net=self._net, item_hash=payload["itemHash"], dyes=dyes 532 ) 533 534 def deserialize_character_render_data( 535 self, payload: typedefs.JSONObject, / 536 ) -> character.RenderedData: 537 return character.RenderedData( 538 net=self._net, 539 customization=self.deserialize_character_customization( 540 payload["customization"] 541 ), 542 custom_dyes=tuple( 543 self.deserialize_character_dye(dye) 544 for dye in payload["customDyes"] 545 if dye 546 ), 547 equipment=tuple( 548 self.deserialize_character_minimal_equipments(equipment) 549 for equipment in payload["peerView"]["equipment"] 550 ), 551 ) 552 553 def deserialize_available_activity( 554 self, payload: typedefs.JSONObject 555 ) -> activity.AvailableActivity: 556 return activity.AvailableActivity( 557 hash=payload["activityHash"], 558 is_new=payload["isNew"], 559 is_completed=payload["isCompleted"], 560 is_visible=payload["isVisible"], 561 display_level=payload.get("displayLevel"), 562 recommended_light=payload.get("recommendedLight"), 563 difficulty=activity.Difficulty(payload["difficultyTier"]), 564 can_join=payload["canJoin"], 565 can_lead=payload["canLead"], 566 ) 567 568 def deserialize_character_activity( 569 self, payload: typedefs.JSONObject 570 ) -> activity.CharacterActivity: 571 current_mode: enums.GameMode | None = None 572 if raw_current_mode := payload.get("currentActivityModeType"): 573 current_mode = enums.GameMode(raw_current_mode) 574 575 if raw_current_modes := payload.get("currentActivityModeTypes"): 576 current_mode_types = tuple( 577 enums.GameMode(type_) for type_ in raw_current_modes 578 ) 579 else: 580 current_mode_types = () 581 582 return activity.CharacterActivity( 583 date_started=time.clean_date(payload["dateActivityStarted"]), 584 current_hash=payload["currentActivityHash"], 585 current_mode_hash=payload["currentActivityModeHash"], 586 current_mode=current_mode, 587 current_mode_hashes=payload.get("currentActivityModeHashes", ()), 588 current_mode_types=current_mode_types, 589 current_playlist_hash=payload.get("currentPlaylistActivityHash"), 590 last_story_hash=payload["lastCompletedStoryHash"], 591 available_activities=tuple( 592 self.deserialize_available_activity(activity_) 593 for activity_ in payload["availableActivities"] 594 ), 595 ) 596 597 def deserialize_profile_items( 598 self, payload: typedefs.JSONObject, / 599 ) -> collections.Sequence[profile.ProfileItemImpl]: 600 return tuple(self.deserialize_profile_item(item) for item in payload["items"]) 601 602 def _deserialize_node(self, payload: typedefs.JSONObject) -> records.Node: 603 return records.Node( 604 state=int(payload["state"]), 605 objective=self.deserialize_objectives(payload["objective"]) 606 if "objective" in payload 607 else None, 608 progress_value=int(payload["progressValue"]), 609 completion_value=int(payload["completionValue"]), 610 record_category_score=int(payload["recordCategoryScore"]) 611 if "recordCategoryScore" in payload 612 else None, 613 ) 614 615 @staticmethod 616 def _deserialize_collectible(payload: typedefs.JSONObject) -> items.Collectible: 617 recent_collectibles: collections.Collection[int] | None = None 618 if raw_recent_collectibles := payload.get("recentCollectibleHashes"): 619 recent_collectibles = tuple( 620 int(item_hash) for item_hash in raw_recent_collectibles 621 ) 622 623 collectibles: dict[int, int] = {} 624 for item_hash, mapping in payload["collectibles"].items(): 625 collectibles[int(item_hash)] = int(mapping["state"]) 626 627 return items.Collectible( 628 recent_collectibles=recent_collectibles, 629 collectibles=collectibles, 630 collection_category_hash=int(payload["collectionCategoriesRootNodeHash"]), 631 collection_badges_hash=int(payload["collectionBadgesRootNodeHash"]), 632 ) 633 634 @staticmethod 635 def _deserialize_currencies( 636 payload: typedefs.JSONObject, 637 ) -> collections.Sequence[items.Currency]: 638 return tuple( 639 items.Currency(hash=int(item_hash), amount=int(amount)) 640 for item_hash, amount in payload["itemQuantities"].items() 641 ) 642 643 def deserialize_progressions( 644 self, payload: typedefs.JSONObject 645 ) -> progressions.Progression: 646 return progressions.Progression( 647 hash=int(payload["progressionHash"]), 648 level=int(payload["level"]), 649 cap=int(payload["levelCap"]), 650 daily_limit=int(payload["dailyLimit"]), 651 weekly_limit=int(payload["weeklyLimit"]), 652 current_progress=int(payload["currentProgress"]), 653 daily_progress=int(payload["dailyProgress"]), 654 needed=int(payload["progressToNextLevel"]), 655 next_level=int(payload["nextLevelAt"]), 656 ) 657 658 def _deserialize_factions( 659 self, payload: typedefs.JSONObject 660 ) -> progressions.Factions: 661 progs = self.deserialize_progressions(payload) 662 return progressions.Factions( 663 hash=progs.hash, 664 level=progs.level, 665 cap=progs.cap, 666 daily_limit=progs.daily_limit, 667 weekly_limit=progs.weekly_limit, 668 current_progress=progs.current_progress, 669 daily_progress=progs.daily_progress, 670 needed=progs.needed, 671 next_level=progs.next_level, 672 faction_hash=payload["factionHash"], 673 faction_vendor_hash=payload["factionVendorIndex"], 674 ) 675 676 def _deserialize_milestone_available_quest( 677 self, payload: typedefs.JSONObject 678 ) -> milestones.MilestoneQuest: 679 return milestones.MilestoneQuest( 680 item_hash=payload["questItemHash"], 681 status=self._deserialize_milestone_quest_status(payload["status"]), 682 ) 683 684 def _deserialize_milestone_activity( 685 self, payload: typedefs.JSONObject 686 ) -> milestones.MilestoneActivity: 687 phases: collections.Sequence[milestones.MilestoneActivityPhase] | None = None 688 if raw_phases := payload.get("phases"): 689 phases = tuple( 690 milestones.MilestoneActivityPhase( 691 is_completed=obj["complete"], hash=obj["phaseHash"] 692 ) 693 for obj in raw_phases 694 ) 695 696 return milestones.MilestoneActivity( 697 hash=payload["activityHash"], 698 challenges=tuple( 699 self.deserialize_objectives(obj["objective"]) 700 for obj in payload["challenges"] 701 ), 702 modifier_hashes=payload.get("modifierHashes"), 703 boolean_options=payload.get("booleanActivityOptions"), 704 phases=phases, 705 ) 706 707 def _deserialize_milestone_quest_status( 708 self, payload: typedefs.JSONObject 709 ) -> milestones.QuestStatus: 710 return milestones.QuestStatus( 711 net=self._net, 712 quest_hash=payload["questHash"], 713 step_hash=payload["stepHash"], 714 step_objectives=tuple( 715 self.deserialize_objectives(objective) 716 for objective in payload["stepObjectives"] 717 ), 718 is_tracked=payload["tracked"], 719 is_completed=payload["completed"], 720 started=payload["started"], 721 item_instance_id=payload["itemInstanceId"], 722 vendor_hash=payload.get("vendorHash"), 723 is_redeemed=payload["redeemed"], 724 ) 725 726 def _deserialize_milestone_rewards( 727 self, payload: typedefs.JSONObject 728 ) -> milestones.MilestoneReward: 729 return milestones.MilestoneReward( 730 category_hash=payload["rewardCategoryHash"], 731 entries=tuple( 732 milestones.MilestoneRewardEntry( 733 entry_hash=entry["rewardEntryHash"], 734 is_earned=entry["earned"], 735 is_redeemed=entry["redeemed"], 736 ) 737 for entry in payload["entries"] 738 ), 739 ) 740 741 def deserialize_milestone( 742 self, payload: typedefs.JSONObject 743 ) -> milestones.Milestone: 744 start_date: datetime.datetime | None = None 745 if raw_start_date := payload.get("startDate"): 746 start_date = time.clean_date(raw_start_date) 747 748 end_date: datetime.datetime | None = None 749 if raw_end_date := payload.get("endDate"): 750 end_date = time.clean_date(raw_end_date) 751 752 rewards: collections.Collection[milestones.MilestoneReward] | None = None 753 if raw_rewards := payload.get("rewards"): 754 rewards = tuple( 755 self._deserialize_milestone_rewards(reward) for reward in raw_rewards 756 ) 757 758 activities: collections.Sequence[milestones.MilestoneActivity] | None = None 759 if raw_activities := payload.get("activities"): 760 activities = tuple( 761 self._deserialize_milestone_activity(active) 762 for active in raw_activities 763 ) 764 765 quests: collections.Sequence[milestones.MilestoneQuest] | None = None 766 if raw_quests := payload.get("availableQuests"): 767 quests = tuple( 768 self._deserialize_milestone_available_quest(quest) 769 for quest in raw_quests 770 ) 771 772 vendors: collections.Sequence[milestones.MilestoneVendor] | None = None 773 if raw_vendors := payload.get("vendors"): 774 vendors = tuple( 775 milestones.MilestoneVendor( 776 vendor_hash=vendor["vendorHash"], 777 preview_itemhash=vendor.get("previewItemHash"), 778 ) 779 for vendor in raw_vendors 780 ) 781 782 return milestones.Milestone( 783 hash=payload["milestoneHash"], 784 start_date=start_date, 785 end_date=end_date, 786 order=payload["order"], 787 rewards=rewards, 788 available_quests=quests, 789 activities=activities, 790 vendors=vendors, 791 ) 792 793 def _deserialize_artifact_tiers( 794 self, payload: typedefs.JSONObject 795 ) -> season.ArtifactTier: 796 return season.ArtifactTier( 797 hash=payload["tierHash"], 798 is_unlocked=payload["isUnlocked"], 799 points_to_unlock=payload["pointsToUnlock"], 800 items=tuple( 801 season.ArtifactTierItem( 802 hash=item["itemHash"], is_active=item["isActive"] 803 ) 804 for item in payload["items"] 805 ), 806 ) 807 808 def deserialize_characters( 809 self, payload: typedefs.JSONObject 810 ) -> collections.Mapping[int, character.Character]: 811 return { 812 int(char_id): self._set_character_attrs(char) 813 for char_id, char in payload["data"].items() 814 } 815 816 def deserialize_character( 817 self, payload: typedefs.JSONObject 818 ) -> character.Character: 819 return self._set_character_attrs(payload) 820 821 def deserialize_character_equipments( 822 self, payload: typedefs.JSONObject 823 ) -> collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]]: 824 return { 825 int(char_id): self.deserialize_profile_items(item) 826 for char_id, item in payload["data"].items() 827 } 828 829 def deserialize_character_activities( 830 self, payload: typedefs.JSONObject 831 ) -> collections.Mapping[int, activity.CharacterActivity]: 832 return { 833 int(char_id): self.deserialize_character_activity(data) 834 for char_id, data in payload["data"].items() 835 } 836 837 def deserialize_characters_render_data( 838 self, payload: typedefs.JSONObject 839 ) -> collections.Mapping[int, character.RenderedData]: 840 return { 841 int(char_id): self.deserialize_character_render_data(data) 842 for char_id, data in payload["data"].items() 843 } 844 845 def deserialize_character_progressions( 846 self, payload: typedefs.JSONObject 847 ) -> character.CharacterProgression: 848 progressions_ = { 849 int(prog_id): self.deserialize_progressions(prog) 850 for prog_id, prog in payload["progressions"].items() 851 } 852 853 factions = { 854 int(faction_id): self._deserialize_factions(faction) 855 for faction_id, faction in payload["factions"].items() 856 } 857 858 milestones_ = { 859 int(milestone_hash): self.deserialize_milestone(milestone) 860 for milestone_hash, milestone in payload["milestones"].items() 861 } 862 863 uninstanced_item_objectives = { 864 int(item_hash): [self.deserialize_objectives(ins) for ins in obj] 865 for item_hash, obj in payload["uninstancedItemObjectives"].items() 866 } 867 868 artifact = payload["seasonalArtifact"] 869 seasonal_artifact = season.CharacterScopedArtifact( 870 hash=artifact["artifactHash"], 871 points_used=artifact["pointsUsed"], 872 reset_count=artifact["resetCount"], 873 tiers=tuple( 874 self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"] 875 ), 876 ) 877 checklists = payload["checklists"] 878 879 return character.CharacterProgression( 880 progressions=progressions_, 881 factions=factions, 882 checklists=checklists, 883 milestones=milestones_, 884 seasonal_artifact=seasonal_artifact, 885 uninstanced_item_objectives=uninstanced_item_objectives, 886 ) 887 888 # fmt: off 889 def deserialize_character_progressions_mapping(self, payload: typedefs.JSONObject) -> collections.Mapping[int, character.CharacterProgression]: 890 character_progressions: collections.MutableMapping[int, character.CharacterProgression] = {} 891 for char_id, data in payload["data"].items(): 892 character_progressions[int(char_id)] = self.deserialize_character_progressions(data) 893 return character_progressions 894 # fmt: on 895 896 def deserialize_characters_records( 897 self, 898 payload: typedefs.JSONObject, 899 ) -> collections.Mapping[int, records.CharacterRecord]: 900 return { 901 int(rec_id): self.deserialize_character_records( 902 rec, record_hashes=payload.get("featuredRecordHashes", ()) 903 ) 904 for rec_id, rec in payload["records"].items() 905 } 906 907 def deserialize_profile_records( 908 self, payload: typedefs.JSONObject 909 ) -> collections.Mapping[int, records.Record]: 910 raw_profile_records = payload["data"] 911 scores = records.RecordScores( 912 current_score=raw_profile_records["score"], 913 legacy_score=raw_profile_records["legacyScore"], 914 lifetime_score=raw_profile_records["lifetimeScore"], 915 ) 916 return { 917 int(record_id): self.deserialize_records( 918 record, 919 scores, 920 categories_hash=raw_profile_records["recordCategoriesRootNodeHash"], 921 seals_hash=raw_profile_records["recordSealsRootNodeHash"], 922 ) 923 for record_id, record in raw_profile_records["records"].items() 924 } 925 926 def _deserialize_craftable_socket_plug( 927 self, payload: typedefs.JSONObject 928 ) -> items.CraftableSocketPlug: 929 return items.CraftableSocketPlug( 930 item_hash=int(payload["plugItemHash"]), 931 failed_requirement_indexes=payload.get("failedRequirementIndexes", ()), 932 ) 933 934 def _deserialize_craftable_socket( 935 self, payload: typedefs.JSONObject 936 ) -> items.CraftableSocket: 937 if raw_plug := payload.get("plug"): 938 plugs = tuple( 939 self._deserialize_craftable_socket_plug(plug) for plug in raw_plug 940 ) 941 else: 942 plugs = () 943 944 return items.CraftableSocket( 945 plug_set_hash=int(payload["plugSetHash"]), plugs=plugs 946 ) 947 948 def _deserialize_craftable_item( 949 self, payload: typedefs.JSONObject 950 ) -> items.CraftableItem: 951 return items.CraftableItem( 952 is_visible=payload["visible"], 953 failed_requirement_indexes=payload.get("failedRequirementIndexes", ()), 954 sockets=tuple( 955 self._deserialize_craftable_socket(socket) 956 for socket in payload["sockets"] 957 ), 958 ) 959 960 def deserialize_craftables_component( 961 self, payload: typedefs.JSONObject 962 ) -> components.CraftablesComponent: 963 return components.CraftablesComponent( 964 net=self._net, 965 craftables={ 966 int(item_id): self._deserialize_craftable_item(item) 967 for item_id, item in payload["craftables"].items() 968 if item is not None 969 }, 970 crafting_root_node_hash=payload["craftingRootNodeHash"], 971 ) 972 973 def deserialize_components( # noqa: C901 Too complex. 974 self, payload: typedefs.JSONObject 975 ) -> components.Component: 976 # Due to how complex this method is, We'll stick to 977 # typing.Optional here. 978 979 profile_: profile.Profile | None = None 980 if raw_profile := payload.get("profile"): 981 profile_ = self.deserialize_profile(raw_profile) 982 983 profile_progression: profile.ProfileProgression | None = None 984 if raw_profile_progression := payload.get("profileProgression"): 985 profile_progression = self.deserialize_profile_progression( 986 raw_profile_progression 987 ) 988 989 profile_currencies: typing.Optional[ 990 collections.Sequence[profile.ProfileItemImpl] 991 ] = None 992 if raw_profile_currencies := payload.get("profileCurrencies"): 993 if "data" in raw_profile_currencies: 994 profile_currencies = self.deserialize_profile_items( 995 raw_profile_currencies["data"] 996 ) 997 998 profile_inventories: typing.Optional[ 999 collections.Sequence[profile.ProfileItemImpl] 1000 ] = None 1001 if raw_profile_inventories := payload.get("profileInventory"): 1002 if "data" in raw_profile_inventories: 1003 profile_inventories = self.deserialize_profile_items( 1004 raw_profile_inventories["data"] 1005 ) 1006 1007 profile_records: typing.Optional[collections.Mapping[int, records.Record]] = ( 1008 None 1009 ) 1010 1011 if raw_profile_records_ := payload.get("profileRecords"): 1012 profile_records = self.deserialize_profile_records(raw_profile_records_) 1013 1014 characters: typing.Optional[collections.Mapping[int, character.Character]] = ( 1015 None 1016 ) 1017 if raw_characters := payload.get("characters"): 1018 characters = self.deserialize_characters(raw_characters) 1019 1020 character_records: typing.Optional[ 1021 collections.Mapping[int, records.CharacterRecord] 1022 ] = None 1023 1024 if raw_character_records := payload.get("characterRecords"): 1025 # Had to do it in two steps.. 1026 to_update = {} 1027 for _, data in raw_character_records["data"].items(): 1028 for record_id, record in data.items(): 1029 to_update[record_id] = record 1030 1031 character_records = { 1032 int(rec_id): self.deserialize_character_records( 1033 rec, record_hashes=to_update.get("featuredRecordHashes", ()) 1034 ) 1035 for rec_id, rec in to_update["records"].items() 1036 } 1037 1038 character_equipments: typing.Optional[ 1039 collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]] 1040 ] = None 1041 if raw_character_equips := payload.get("characterEquipment"): 1042 character_equipments = self.deserialize_character_equipments( 1043 raw_character_equips 1044 ) 1045 1046 character_inventories: typing.Optional[ 1047 collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]] 1048 ] = None 1049 if raw_character_inventories := payload.get("characterInventories"): 1050 if "data" in raw_character_inventories: 1051 character_inventories = self.deserialize_character_equipments( 1052 raw_character_inventories 1053 ) 1054 1055 character_activities: typing.Optional[ 1056 collections.Mapping[int, activity.CharacterActivity] 1057 ] = None 1058 if raw_char_acts := payload.get("characterActivities"): 1059 character_activities = self.deserialize_character_activities(raw_char_acts) 1060 1061 character_render_data: typing.Optional[ 1062 collections.Mapping[int, character.RenderedData] 1063 ] = None 1064 if raw_character_render_data := payload.get("characterRenderData"): 1065 character_render_data = self.deserialize_characters_render_data( 1066 raw_character_render_data 1067 ) 1068 1069 character_progressions: typing.Optional[ 1070 collections.Mapping[int, character.CharacterProgression] 1071 ] = None 1072 1073 if raw_character_progressions := payload.get("characterProgressions"): 1074 character_progressions = self.deserialize_character_progressions_mapping( 1075 raw_character_progressions 1076 ) 1077 1078 profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None 1079 if raw_profile_string_vars := payload.get("profileStringVariables"): 1080 profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"] 1081 1082 character_string_vars: typing.Optional[ 1083 collections.Mapping[int, collections.Mapping[int, int]] 1084 ] = None 1085 if raw_character_string_vars := payload.get("characterStringVariables"): 1086 character_string_vars = { 1087 int(char_id): data["integerValuesByHash"] 1088 for char_id, data in raw_character_string_vars["data"].items() 1089 } 1090 1091 metrics: typing.Optional[ 1092 collections.Sequence[ 1093 collections.Mapping[int, tuple[bool, records.Objective | None]] 1094 ] 1095 ] = None 1096 root_node_hash: int | None = None 1097 1098 if raw_metrics := payload.get("metrics"): 1099 root_node_hash = raw_metrics["data"]["metricsRootNodeHash"] 1100 metrics = tuple( 1101 { 1102 int(metrics_hash): ( 1103 data["invisible"], 1104 self.deserialize_objectives(data["objectiveProgress"]) 1105 if "objectiveProgress" in data 1106 else None, 1107 ) 1108 } 1109 for metrics_hash, data in raw_metrics["data"]["metrics"].items() 1110 ) 1111 transitory: fireteams.FireteamParty | None = None 1112 if raw_transitory := payload.get("profileTransitoryData"): 1113 if "data" in raw_transitory: 1114 transitory = self.deserialize_fireteam_party(raw_transitory["data"]) 1115 1116 item_components: components.ItemsComponent | None = None 1117 if raw_item_components := payload.get("itemComponents"): 1118 item_components = self.deserialize_items_component(raw_item_components) 1119 1120 profile_plugsets: typing.Optional[ 1121 collections.Mapping[int, collections.Sequence[items.PlugItemState]] 1122 ] = None 1123 1124 if raw_profile_plugs := payload.get("profilePlugSets"): 1125 profile_plugsets = { 1126 int(index): [self.deserialize_plug_item_state(state) for state in data] 1127 for index, data in raw_profile_plugs["data"]["plugs"].items() 1128 } 1129 1130 character_plugsets: typing.Optional[ 1131 collections.Mapping[ 1132 int, collections.Mapping[int, collections.Sequence[items.PlugItemState]] 1133 ] 1134 ] = None 1135 if raw_char_plugsets := payload.get("characterPlugSets"): 1136 character_plugsets = { 1137 int(char_id): { 1138 int(index): [ 1139 self.deserialize_plug_item_state(state) for state in data 1140 ] 1141 for index, data in inner["plugs"].items() 1142 } 1143 for char_id, inner in raw_char_plugsets["data"].items() 1144 } 1145 1146 character_collectibles: typing.Optional[ 1147 collections.Mapping[int, items.Collectible] 1148 ] = None 1149 if raw_character_collectibles := payload.get("characterCollectibles"): 1150 character_collectibles = { 1151 int(char_id): self._deserialize_collectible(data) 1152 for char_id, data in raw_character_collectibles["data"].items() 1153 } 1154 1155 profile_collectibles: items.Collectible | None = None 1156 if raw_profile_collectibles := payload.get("profileCollectibles"): 1157 profile_collectibles = self._deserialize_collectible( 1158 raw_profile_collectibles["data"] 1159 ) 1160 1161 profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None 1162 if raw_profile_nodes := payload.get("profilePresentationNodes"): 1163 profile_nodes = { 1164 int(node_hash): self._deserialize_node(node) 1165 for node_hash, node in raw_profile_nodes["data"]["nodes"].items() 1166 } 1167 1168 character_nodes: typing.Optional[ 1169 collections.Mapping[int, collections.Mapping[int, records.Node]] 1170 ] = None 1171 if raw_character_nodes := payload.get("characterPresentationNodes"): 1172 character_nodes = { 1173 int(char_id): { 1174 int(node_hash): self._deserialize_node(node) 1175 for node_hash, node in each_character["nodes"].items() 1176 } 1177 for char_id, each_character in raw_character_nodes["data"].items() 1178 } 1179 1180 platform_silver: typing.Optional[ 1181 collections.Mapping[str, profile.ProfileItemImpl] 1182 ] = None 1183 if raw_platform_silver := payload.get("platformSilver"): 1184 if "data" in raw_platform_silver: 1185 platform_silver = { 1186 platform_name: self.deserialize_profile_item(item) 1187 for platform_name, item in raw_platform_silver["data"][ 1188 "platformSilver" 1189 ].items() 1190 } 1191 1192 character_currency_lookups: typing.Optional[ 1193 collections.Mapping[int, collections.Sequence[items.Currency]] 1194 ] = None 1195 if raw_char_lookups := payload.get("characterCurrencyLookups"): 1196 if "data" in raw_char_lookups: 1197 character_currency_lookups = { 1198 int(char_id): self._deserialize_currencies(currency) 1199 for char_id, currency in raw_char_lookups["data"].items() 1200 } 1201 1202 character_craftables: typing.Optional[ 1203 collections.Mapping[int, components.CraftablesComponent] 1204 ] = None 1205 if raw_character_craftables := payload.get("characterCraftables"): 1206 if "data" in raw_character_craftables: 1207 character_craftables = { 1208 int(char_id): self.deserialize_craftables_component(craftable) 1209 for char_id, craftable in raw_character_craftables["data"].items() 1210 } 1211 1212 return components.Component( 1213 profiles=profile_, 1214 profile_progression=profile_progression, 1215 profile_currencies=profile_currencies, 1216 profile_inventories=profile_inventories, 1217 profile_records=profile_records, 1218 characters=characters, 1219 character_records=character_records, 1220 character_equipments=character_equipments, 1221 character_inventories=character_inventories, 1222 character_activities=character_activities, 1223 character_render_data=character_render_data, 1224 character_progressions=character_progressions, 1225 profile_string_variables=profile_string_vars, 1226 character_string_variables=character_string_vars, 1227 metrics=metrics, 1228 root_node_hash=root_node_hash, 1229 transitory=transitory, 1230 item_components=item_components, 1231 profile_plugsets=profile_plugsets, 1232 character_plugsets=character_plugsets, 1233 character_collectibles=character_collectibles, 1234 profile_collectibles=profile_collectibles, 1235 profile_nodes=profile_nodes, 1236 character_nodes=character_nodes, 1237 platform_silver=platform_silver, 1238 character_currency_lookups=character_currency_lookups, 1239 character_craftables=character_craftables, 1240 ) 1241 1242 def deserialize_items_component( 1243 self, payload: typedefs.JSONObject 1244 ) -> components.ItemsComponent: 1245 # Due to how complex this method is, We'll stick to typing.Optional. 1246 instances: typing.Optional[ 1247 collections.Sequence[collections.Mapping[int, items.ItemInstance]] 1248 ] = None 1249 if raw_instances := payload.get("instances"): 1250 instances = tuple( 1251 {int(ins_id): self.deserialize_instanced_item(item)} 1252 for ins_id, item in raw_instances["data"].items() 1253 ) 1254 1255 render_data: typing.Optional[ 1256 collections.Mapping[int, tuple[bool, dict[int, int]]] 1257 ] = None 1258 if raw_render_data := payload.get("renderData"): 1259 render_data = { 1260 int(ins_id): (data["useCustomDyes"], data["artRegions"]) 1261 for ins_id, data in raw_render_data["data"].items() 1262 } 1263 1264 stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None 1265 if raw_stats := payload.get("stats"): 1266 stats = {} 1267 for ins_id, stat in raw_stats["data"].items(): 1268 for _, items_ in stat.items(): 1269 stats[int(ins_id)] = self.deserialize_item_stats_view(items_) 1270 1271 sockets: typing.Optional[ 1272 collections.Mapping[int, collections.Sequence[items.ItemSocket]] 1273 ] = None 1274 if raw_sockets := payload.get("sockets"): 1275 sockets = { 1276 int(ins_id): tuple( 1277 self.deserialize_item_socket(socket) for socket in item["sockets"] 1278 ) 1279 for ins_id, item in raw_sockets["data"].items() 1280 } 1281 1282 objectives: typing.Optional[ 1283 collections.Mapping[int, collections.Sequence[records.Objective]] 1284 ] = None 1285 if raw_objectives := payload.get("objectives"): 1286 objectives = { 1287 int(ins_id): tuple( 1288 self.deserialize_objectives(objective) 1289 for objective in data["objectives"] 1290 ) 1291 for ins_id, data in raw_objectives["data"].items() 1292 } 1293 1294 perks: typing.Optional[ 1295 collections.Mapping[int, collections.Collection[items.ItemPerk]] 1296 ] = None 1297 if raw_perks := payload.get("perks"): 1298 perks = { 1299 int(ins_id): tuple( 1300 self.deserialize_item_perk(perk) for perk in item["perks"] 1301 ) 1302 for ins_id, item in raw_perks["data"].items() 1303 } 1304 1305 plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None 1306 if raw_plug_states := payload.get("plugStates"): 1307 plug_states = tuple( 1308 self.deserialize_plug_item_state(plug) 1309 for _, plug in raw_plug_states["data"].items() 1310 ) 1311 1312 reusable_plugs: typing.Optional[ 1313 collections.Mapping[int, collections.Sequence[items.PlugItemState]] 1314 ] = None 1315 if raw_re_plugs := payload.get("reusablePlugs"): 1316 reusable_plugs = { 1317 int(ins_id): tuple( 1318 self.deserialize_plug_item_state(state) for state in inner 1319 ) 1320 for ins_id, plug in raw_re_plugs["data"].items() 1321 for inner in tuple(plug["plugs"].values()) 1322 } 1323 1324 plug_objectives: typing.Optional[ 1325 collections.Mapping[ 1326 int, collections.Mapping[int, collections.Collection[records.Objective]] 1327 ] 1328 ] = None 1329 if raw_plug_objectives := payload.get("plugObjectives"): 1330 plug_objectives = { 1331 int(ins_id): { 1332 int(obj_hash): tuple( 1333 self.deserialize_objectives(obj) for obj in objs 1334 ) 1335 for obj_hash, objs in inner["objectivesPerPlug"].items() 1336 } 1337 for ins_id, inner in raw_plug_objectives["data"].items() 1338 } 1339 1340 return components.ItemsComponent( 1341 sockets=sockets, 1342 stats=stats, 1343 render_data=render_data, 1344 instances=instances, 1345 objectives=objectives, 1346 perks=perks, 1347 plug_states=plug_states, 1348 reusable_plugs=reusable_plugs, 1349 plug_objectives=plug_objectives, 1350 ) 1351 1352 def deserialize_character_component( 1353 self, payload: typedefs.JSONObject 1354 ) -> components.CharacterComponent: 1355 character_: character.Character | None = None 1356 if raw_singular_character := payload.get("character"): 1357 character_ = self.deserialize_character(raw_singular_character["data"]) 1358 1359 inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None 1360 if raw_inventory := payload.get("inventory"): 1361 if "data" in raw_inventory: 1362 inventory = self.deserialize_profile_items(raw_inventory["data"]) 1363 1364 activities: activity.CharacterActivity | None = None 1365 if raw_activities := payload.get("activities"): 1366 activities = self.deserialize_character_activity(raw_activities["data"]) 1367 1368 equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None 1369 if raw_equipments := payload.get("equipment"): 1370 equipment = self.deserialize_profile_items(raw_equipments["data"]) 1371 1372 progressions_: character.CharacterProgression | None = None 1373 if raw_progressions := payload.get("progressions"): 1374 progressions_ = self.deserialize_character_progressions( 1375 raw_progressions["data"] 1376 ) 1377 1378 render_data: character.RenderedData | None = None 1379 if raw_render_data := payload.get("renderData"): 1380 render_data = self.deserialize_character_render_data( 1381 raw_render_data["data"] 1382 ) 1383 1384 character_records: typing.Optional[ 1385 collections.Mapping[int, records.CharacterRecord] 1386 ] = None 1387 if raw_char_records := payload.get("records"): 1388 character_records = self.deserialize_characters_records( 1389 raw_char_records["data"] 1390 ) 1391 1392 item_components: components.ItemsComponent | None = None 1393 if raw_item_components := payload.get("itemComponents"): 1394 item_components = self.deserialize_items_component(raw_item_components) 1395 1396 nodes: typing.Optional[collections.Mapping[int, records.Node]] = None 1397 if raw_nodes := payload.get("presentationNodes"): 1398 nodes = { 1399 int(node_hash): self._deserialize_node(node) 1400 for node_hash, node in raw_nodes["data"]["nodes"].items() 1401 } 1402 1403 collectibles: items.Collectible | None = None 1404 if raw_collectibles := payload.get("collectibles"): 1405 collectibles = self._deserialize_collectible(raw_collectibles["data"]) 1406 1407 currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None 1408 if raw_currencies := payload.get("currencyLookups"): 1409 if "data" in raw_currencies: 1410 currency_lookups = self._deserialize_currencies(raw_currencies) 1411 1412 return components.CharacterComponent( 1413 activities=activities, 1414 equipment=equipment, 1415 inventory=inventory, 1416 progressions=progressions_, 1417 render_data=render_data, 1418 character=character_, 1419 character_records=character_records, 1420 profile_records=None, 1421 item_components=item_components, 1422 currency_lookups=currency_lookups, 1423 collectibles=collectibles, 1424 nodes=nodes, 1425 ) 1426 1427 def _set_entity_attrs( 1428 self, payload: typedefs.JSONObject, *, key: str = "displayProperties" 1429 ) -> entity.Entity: 1430 properties = payload[key] 1431 name = typedefs.unknown(properties["name"]) 1432 description = typedefs.unknown(properties["description"]) 1433 1434 return entity.Entity( 1435 net=self._net, 1436 hash=payload["hash"], 1437 index=payload["index"], 1438 name=name, 1439 description=description, 1440 has_icon=properties["hasIcon"], 1441 icon=assets.Image.default_or_else(properties.get("icon")), 1442 ) 1443 1444 def deserialize_inventory_results( 1445 self, payload: typedefs.JSONObject 1446 ) -> iterators.Iterator[entity.SearchableEntity]: 1447 return iterators.Iterator( 1448 [ 1449 entity.SearchableEntity( 1450 net=self._net, 1451 hash=data["hash"], 1452 entity_type=data["entityType"], 1453 weight=data["weight"], 1454 suggested_words=payload["suggestedWords"], 1455 name=data["displayProperties"]["name"], 1456 has_icon=data["displayProperties"]["hasIcon"], 1457 description=typedefs.unknown( 1458 data["displayProperties"]["description"] 1459 ), 1460 icon=assets.Image(path=data["displayProperties"]["icon"]), 1461 ) 1462 for data in payload["results"]["results"] 1463 ] 1464 ) 1465 1466 def _deserialize_inventory_item_objects( 1467 self, payload: typedefs.JSONObject 1468 ) -> entity.InventoryEntityObjects: 1469 return entity.InventoryEntityObjects( 1470 action=payload.get("action"), 1471 set_data=payload.get("setData"), 1472 stats=payload.get("stats"), 1473 equipping_block=payload.get("equippingBlock"), 1474 translation_block=payload.get("translationBlock"), 1475 preview=payload.get("preview"), 1476 quality=payload.get("quality"), 1477 value=payload.get("value"), 1478 source_data=payload.get("sourceData"), 1479 objectives=payload.get("objectives"), 1480 plug=payload.get("plug"), 1481 metrics=payload.get("metrics"), 1482 gearset=payload.get("gearset"), 1483 sack=payload.get("sack"), 1484 sockets=payload.get("sockets"), 1485 summary=payload.get("summary"), 1486 talent_gird=payload.get("talentGrid"), 1487 investments_stats=payload.get("investmentStats"), 1488 perks=payload.get("perks"), 1489 animations=payload.get("animations", ()), 1490 links=payload.get("links", ()), 1491 ) 1492 1493 def deserialize_inventory_entity( # noqa: C901 Too complex. 1494 self, payload: typedefs.JSONObject, / 1495 ) -> entity.InventoryEntity: 1496 props = self._set_entity_attrs(payload) 1497 objects = self._deserialize_inventory_item_objects(payload) 1498 1499 collectible_hash: int | None = None 1500 if raw_collectible_hash := payload.get("collectibleHash"): 1501 collectible_hash = int(raw_collectible_hash) 1502 1503 secondary_icon: assets.Image | None = None 1504 if raw_second_icon := payload.get("secondaryIcon"): 1505 secondary_icon = assets.Image(path=raw_second_icon) 1506 1507 secondary_overlay: assets.Image | None = None 1508 if raw_second_overlay := payload.get("secondaryOverlay"): 1509 secondary_overlay = assets.Image(path=raw_second_overlay) 1510 1511 secondary_special: assets.Image | None = None 1512 if raw_second_special := payload.get("secondarySpecial"): 1513 secondary_special = assets.Image(path=raw_second_special) 1514 1515 screenshot: assets.Image | None = None 1516 if raw_screenshot := payload.get("screenshot"): 1517 screenshot = assets.Image(path=raw_screenshot) 1518 1519 watermark_icon: assets.Image | None = None 1520 if raw_watermark_icon := payload.get("iconWatermark"): 1521 watermark_icon = assets.Image(path=raw_watermark_icon) 1522 1523 watermark_shelved: assets.Image | None = None 1524 if raw_watermark_shelved := payload.get("iconWatermarkShelved"): 1525 watermark_shelved = assets.Image(path=raw_watermark_shelved) 1526 1527 about: str | None = None 1528 if raw_about := payload.get("flavorText"): 1529 about = raw_about 1530 1531 ui_item_style: str | None = None 1532 if raw_ui_style := payload.get("uiItemDisplayStyle"): 1533 ui_item_style = raw_ui_style 1534 1535 tier_and_name: str | None = None 1536 if raw_tier_and_name := payload.get("itemTypeAndTierDisplayName"): 1537 tier_and_name = raw_tier_and_name 1538 1539 type_name: str | None = None 1540 if raw_type_name := payload.get("itemTypeDisplayName"): 1541 type_name = raw_type_name 1542 1543 display_source: str | None = None 1544 if raw_display_source := payload.get("displaySource"): 1545 display_source = raw_display_source 1546 1547 lorehash: int | None = None 1548 if raw_lore_hash := payload.get("loreHash"): 1549 lorehash = int(raw_lore_hash) 1550 1551 summary_hash: int | None = None 1552 if raw_summary_hash := payload.get("summaryItemHash"): 1553 summary_hash = raw_summary_hash 1554 1555 breaker_type_hash: int | None = None 1556 if raw_breaker_type_hash := payload.get("breakerTypeHash"): 1557 breaker_type_hash = int(raw_breaker_type_hash) 1558 1559 damage_types: typing.Optional[collections.Sequence[int]] = None 1560 if raw_damage_types := payload.get("damageTypes"): 1561 damage_types = tuple(int(type_) for type_ in raw_damage_types) 1562 1563 damagetype_hashes: typing.Optional[collections.Sequence[int]] = None 1564 if raw_damagetype_hashes := payload.get("damageTypeHashes"): 1565 damagetype_hashes = tuple(int(type_) for type_ in raw_damagetype_hashes) 1566 1567 default_damagetype_hash: int | None = None 1568 if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"): 1569 default_damagetype_hash = int(raw_defaultdmg_hash) 1570 1571 emblem_objective_hash: int | None = None 1572 if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"): 1573 emblem_objective_hash = int(raw_emblem_obj_hash) 1574 1575 tier_type: enums.TierType | None = None 1576 tier: enums.ItemTier | None = None 1577 bucket_hash: int | None = None 1578 recovery_hash: int | None = None 1579 tier_name: str | None = None 1580 isinstance_item: bool = False 1581 expire_tool_tip: str | None = None 1582 expire_in_orbit_message: str | None = None 1583 suppress_expiration: bool = False 1584 max_stack_size: int | None = None 1585 stack_label: str | None = None 1586 1587 if inventory := payload.get("inventory"): 1588 tier_type = enums.TierType(int(inventory["tierType"])) 1589 tier = enums.ItemTier(int(inventory["tierTypeHash"])) 1590 bucket_hash = int(inventory["bucketTypeHash"]) 1591 recovery_hash = int(inventory["recoveryBucketTypeHash"]) 1592 tier_name = inventory["tierTypeName"] 1593 isinstance_item = inventory["isInstanceItem"] 1594 suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"] 1595 max_stack_size = int(inventory["maxStackSize"]) 1596 1597 try: 1598 stack_label = inventory["stackUniqueLabel"] 1599 except KeyError: 1600 pass 1601 1602 if "traitHashes" in payload: 1603 trait_hashes = tuple( 1604 int(trait_hash) for trait_hash in payload["traitHashes"] 1605 ) 1606 else: 1607 trait_hashes = () 1608 1609 if "traitIds" in payload: 1610 trait_ids = tuple(trait_id for trait_id in payload["traitIds"]) 1611 else: 1612 trait_ids = () 1613 1614 return entity.InventoryEntity( 1615 net=self._net, 1616 collectible_hash=collectible_hash, 1617 name=props.name, 1618 about=about, 1619 emblem_objective_hash=emblem_objective_hash, 1620 suppress_expiration=suppress_expiration, 1621 max_stack_size=max_stack_size, 1622 stack_label=stack_label, 1623 tier=tier, 1624 tier_type=tier_type, 1625 tier_name=tier_name, 1626 bucket_hash=bucket_hash, 1627 recovery_bucket_hash=recovery_hash, 1628 isinstance_item=isinstance_item, 1629 expire_in_orbit_message=expire_in_orbit_message, 1630 expiration_tooltip=expire_tool_tip, 1631 lore_hash=lorehash, 1632 type_and_tier_name=tier_and_name, 1633 summary_hash=summary_hash, 1634 ui_display_style=ui_item_style, 1635 type_name=type_name, 1636 breaker_type_hash=breaker_type_hash, 1637 description=props.description, 1638 display_source=display_source, 1639 hash=props.hash, 1640 damage_types=damage_types, 1641 index=props.index, 1642 icon=props.icon, 1643 has_icon=props.has_icon, 1644 screenshot=screenshot, 1645 watermark_icon=watermark_icon, 1646 watermark_shelved=watermark_shelved, 1647 secondary_icon=secondary_icon, 1648 secondary_overlay=secondary_overlay, 1649 secondary_special=secondary_special, 1650 type=enums.ItemType(int(payload["itemType"])), 1651 category_hashes=tuple( 1652 int(hash_) for hash_ in payload["itemCategoryHashes"] 1653 ), 1654 item_class=enums.Class(int(payload["classType"])), 1655 sub_type=enums.ItemSubType(int(payload["itemSubType"])), 1656 breaker_type=int(payload["breakerType"]), 1657 default_damagetype=int(payload["defaultDamageType"]), 1658 default_damagetype_hash=default_damagetype_hash, 1659 damagetype_hashes=damagetype_hashes, 1660 tooltip_notifications=payload["tooltipNotifications"], 1661 not_transferable=payload["nonTransferrable"], 1662 allow_actions=payload["allowActions"], 1663 is_equippable=payload["equippable"], 1664 objects=objects, 1665 background_colors=payload.get("backgroundColor", {}), 1666 season_hash=payload.get("seasonHash"), 1667 has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"], 1668 trait_hashes=trait_hashes, 1669 trait_ids=trait_ids, 1670 ) 1671 1672 def deserialize_objective_entity( 1673 self, payload: typedefs.JSONObject, / 1674 ) -> entity.ObjectiveEntity: 1675 props = self._set_entity_attrs(payload) 1676 return entity.ObjectiveEntity( 1677 net=self._net, 1678 hash=props.hash, 1679 index=props.index, 1680 description=props.description, 1681 name=props.name, 1682 has_icon=props.has_icon, 1683 icon=props.icon, 1684 unlock_value_hash=payload["unlockValueHash"], 1685 completion_value=payload["completionValue"], 1686 scope=entity.GatingScope(int(payload["scope"])), 1687 location_hash=payload["locationHash"], 1688 allowed_negative_value=payload["allowNegativeValue"], 1689 allowed_value_change=payload["allowValueChangeWhenCompleted"], 1690 counting_downward=payload["isCountingDownward"], 1691 value_style=entity.ValueUIStyle(int(payload["valueStyle"])), 1692 progress_description=payload["progressDescription"], 1693 perks=payload["perks"], 1694 stats=payload["stats"], 1695 minimum_visibility=payload["minimumVisibilityThreshold"], 1696 allow_over_completion=payload["allowOvercompletion"], 1697 show_value_style=payload["showValueOnComplete"], 1698 display_only_objective=payload["isDisplayOnlyObjective"], 1699 complete_value_style=entity.ValueUIStyle( 1700 int(payload["completedValueStyle"]) 1701 ), 1702 progress_value_style=entity.ValueUIStyle( 1703 int(payload["inProgressValueStyle"]) 1704 ), 1705 ui_label=payload["uiLabel"], 1706 ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])), 1707 ) 1708 1709 def _deserialize_activity_values( 1710 self, payload: typedefs.JSONObject, / 1711 ) -> activity.ActivityValues: 1712 team: int | None = None 1713 if raw_team := payload.get("team"): 1714 team = raw_team["basic"]["value"] 1715 return activity.ActivityValues( 1716 assists=payload["assists"]["basic"]["value"], 1717 deaths=payload["deaths"]["basic"]["value"], 1718 kills=payload["kills"]["basic"]["value"], 1719 is_completed=bool(payload["completed"]["basic"]["value"]), 1720 opponents_defeated=payload["opponentsDefeated"]["basic"]["value"], 1721 efficiency=payload["efficiency"]["basic"]["value"], 1722 kd_ratio=payload["killsDeathsRatio"]["basic"]["value"], 1723 kd_assists=payload["killsDeathsAssists"]["basic"]["value"], 1724 score=payload["score"]["basic"]["value"], 1725 duration=payload["activityDurationSeconds"]["basic"]["displayValue"], 1726 team=team, 1727 completion_reason=payload["completionReason"]["basic"]["displayValue"], 1728 fireteam_id=payload["fireteamId"]["basic"]["value"], 1729 start_seconds=payload["startSeconds"]["basic"]["value"], 1730 played_time=payload["timePlayedSeconds"]["basic"]["displayValue"], 1731 player_count=payload["playerCount"]["basic"]["value"], 1732 team_score=payload["teamScore"]["basic"]["value"], 1733 ) 1734 1735 def deserialize_activity( 1736 self, 1737 payload: typedefs.JSONObject, 1738 /, 1739 ) -> activity.Activity: 1740 period = time.clean_date(payload["period"]) 1741 details = payload["activityDetails"] 1742 ref_id = int(details["referenceId"]) 1743 instance_id = int(details["instanceId"]) 1744 mode = enums.GameMode(details["mode"]) 1745 modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"]) 1746 is_private = details["isPrivate"] 1747 membership_type = enums.MembershipType(int(details["membershipType"])) 1748 1749 # Since we're using the same fields for post activity method 1750 # this check is required since post activity doesn't values values 1751 values = self._deserialize_activity_values(payload["values"]) 1752 1753 return activity.Activity( 1754 net=self._net, 1755 hash=ref_id, 1756 instance_id=instance_id, 1757 mode=mode, 1758 modes=modes, 1759 is_private=is_private, 1760 membership_type=membership_type, 1761 occurred_at=period, 1762 values=values, 1763 ) 1764 1765 def deserialize_activities( 1766 self, payload: typedefs.JSONObject 1767 ) -> iterators.Iterator[activity.Activity]: 1768 return iterators.Iterator( 1769 [ 1770 self.deserialize_activity(activity_) 1771 for activity_ in payload["activities"] 1772 ] 1773 ) 1774 1775 def deserialize_extended_weapon_values( 1776 self, payload: typedefs.JSONObject 1777 ) -> activity.ExtendedWeaponValues: 1778 assists: int | None = None 1779 if raw_assists := payload["values"].get("uniqueWeaponAssists"): 1780 assists = raw_assists["basic"]["value"] 1781 assists_damage: int | None = None 1782 1783 if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"): 1784 assists_damage = raw_assists_damage["basic"]["value"] 1785 1786 return activity.ExtendedWeaponValues( 1787 reference_id=int(payload["referenceId"]), 1788 kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"], 1789 precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][ 1790 "value" 1791 ], 1792 assists=assists, 1793 assists_damage=assists_damage, 1794 precision_kills_percentage=( 1795 payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"], 1796 payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][ 1797 "displayValue" 1798 ], 1799 ), 1800 ) 1801 1802 def _deserialize_extended_values( 1803 self, payload: typedefs.JSONObject 1804 ) -> activity.ExtendedValues: 1805 if raw_weapons := payload.get("weapons"): 1806 weapons = tuple( 1807 self.deserialize_extended_weapon_values(value) for value in raw_weapons 1808 ) 1809 else: 1810 weapons = () 1811 1812 return activity.ExtendedValues( 1813 precision_kills=payload["values"]["precisionKills"]["basic"]["value"], 1814 grenade_kills=payload["values"]["weaponKillsGrenade"]["basic"]["value"], 1815 melee_kills=payload["values"]["weaponKillsMelee"]["basic"]["value"], 1816 super_kills=payload["values"]["weaponKillsSuper"]["basic"]["value"], 1817 ability_kills=payload["values"]["weaponKillsAbility"]["basic"]["value"], 1818 weapons=weapons, 1819 ) 1820 1821 def deserialize_post_activity_player( 1822 self, payload: typedefs.JSONObject, / 1823 ) -> activity.PostActivityPlayer: 1824 player = payload["player"] 1825 1826 class_hash: int | None = None 1827 if (class_hash := player.get("classHash")) is not None: 1828 class_hash = class_hash 1829 1830 race_hash: int | None = None 1831 if (race_hash := player.get("raceHash")) is not None: 1832 race_hash = race_hash 1833 1834 gender_hash: int | None = None 1835 if (gender_hash := player.get("genderHash")) is not None: 1836 gender_hash = gender_hash 1837 1838 character_class: str | None = None 1839 if character_class := player.get("characterClass"): 1840 character_class = character_class 1841 1842 character_level: int | None = None 1843 if (character_level := player.get("characterLevel")) is not None: 1844 character_level = character_level 1845 1846 return activity.PostActivityPlayer( 1847 standing=int(payload["standing"]), 1848 score=int(payload["score"]["basic"]["value"]), 1849 character_id=payload["characterId"], 1850 destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]), 1851 character_class=character_class, 1852 character_level=character_level, 1853 race_hash=race_hash, 1854 gender_hash=gender_hash, 1855 class_hash=class_hash, 1856 light_level=int(player["lightLevel"]), 1857 emblem_hash=int(player["emblemHash"]), 1858 values=self._deserialize_activity_values(payload["values"]), 1859 extended_values=self._deserialize_extended_values(payload["extended"]), 1860 ) 1861 1862 def _deserialize_post_activity_team( 1863 self, payload: typedefs.JSONObject 1864 ) -> activity.PostActivityTeam: 1865 return activity.PostActivityTeam( 1866 id=payload["teamId"], 1867 is_defeated=bool(payload["standing"]["basic"]["value"]), 1868 score=int(payload["score"]["basic"]["value"]), 1869 name=payload["teamName"], 1870 ) 1871 1872 def deserialize_post_activity( 1873 self, payload: typedefs.JSONObject 1874 ) -> activity.PostActivity: 1875 period = time.clean_date(payload["period"]) 1876 details = payload["activityDetails"] 1877 ref_id = int(details["referenceId"]) 1878 instance_id = int(details["instanceId"]) 1879 mode = enums.GameMode(details["mode"]) 1880 modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"]) 1881 is_private = details["isPrivate"] 1882 membership_type = enums.MembershipType(int(details["membershipType"])) 1883 return activity.PostActivity( 1884 net=self._net, 1885 hash=ref_id, 1886 membership_type=membership_type, 1887 instance_id=instance_id, 1888 mode=mode, 1889 modes=modes, 1890 is_private=is_private, 1891 occurred_at=period, 1892 starting_phase=int(payload["startingPhaseIndex"]), 1893 players=tuple( 1894 self.deserialize_post_activity_player(player) 1895 for player in payload["entries"] 1896 ), 1897 teams=tuple( 1898 self._deserialize_post_activity_team(team) for team in payload["teams"] 1899 ), 1900 ) 1901 1902 def _deserialize_aggregated_activity_values( 1903 self, payload: typedefs.JSONObject 1904 ) -> activity.AggregatedActivityValues: 1905 # This ID is always the same for all aggregated values. 1906 activity_id = int(payload["fastestCompletionMsForActivity"]["activityId"]) 1907 1908 return activity.AggregatedActivityValues( 1909 id=activity_id, 1910 fastest_completion_time=( 1911 int(payload["fastestCompletionMsForActivity"]["basic"]["value"]), 1912 payload["fastestCompletionMsForActivity"]["basic"]["displayValue"], 1913 ), 1914 completions=int(payload["activityCompletions"]["basic"]["value"]), 1915 kills=int(payload["activityKills"]["basic"]["value"]), 1916 deaths=int(payload["activityDeaths"]["basic"]["value"]), 1917 assists=int(payload["activityAssists"]["basic"]["value"]), 1918 seconds_played=( 1919 int(payload["activitySecondsPlayed"]["basic"]["value"]), 1920 payload["activitySecondsPlayed"]["basic"]["displayValue"], 1921 ), 1922 wins=int(payload["activityWins"]["basic"]["value"]), 1923 goals_missed=int(payload["activityGoalsMissed"]["basic"]["value"]), 1924 special_actions=int(payload["activitySpecialActions"]["basic"]["value"]), 1925 best_goals_hit=int(payload["activityBestGoalsHit"]["basic"]["value"]), 1926 best_single_score=int( 1927 payload["activityBestSingleGameScore"]["basic"]["value"] 1928 ), 1929 goals_hit=int(payload["activityGoalsHit"]["basic"]["value"]), 1930 special_score=int(payload["activitySpecialScore"]["basic"]["value"]), 1931 kd_assists=int(payload["activityKillsDeathsAssists"]["basic"]["value"]), 1932 kd_ratio=float( 1933 payload["activityKillsDeathsAssists"]["basic"]["displayValue"] 1934 ), 1935 precision_kills=int(payload["activityPrecisionKills"]["basic"]["value"]), 1936 ) 1937 1938 def deserialize_aggregated_activity( 1939 self, payload: typedefs.JSONObject 1940 ) -> activity.AggregatedActivity: 1941 return activity.AggregatedActivity( 1942 hash=int(payload["activityHash"]), 1943 values=self._deserialize_aggregated_activity_values(payload["values"]), 1944 ) 1945 1946 def deserialize_aggregated_activities( 1947 self, payload: typedefs.JSONObject 1948 ) -> iterators.Iterator[activity.AggregatedActivity]: 1949 return iterators.Iterator( 1950 [ 1951 self.deserialize_aggregated_activity(activity) 1952 for activity in payload["activities"] 1953 ] 1954 ) 1955 1956 def deserialize_linked_profiles( 1957 self, payload: typedefs.JSONObject 1958 ) -> profile.LinkedProfile: 1959 bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"]) 1960 1961 if raw_profile := payload.get("profiles"): 1962 profiles = tuple( 1963 self.deserialize_destiny_membership(p) for p in raw_profile 1964 ) 1965 else: 1966 profiles = () 1967 1968 error_profiles = () 1969 if raw_profiles_with_errors := payload.get("profilesWithErrors"): 1970 for raw_error_p in raw_profiles_with_errors: 1971 if "infoCard" in raw_error_p: 1972 error_profiles = tuple( 1973 self.deserialize_destiny_membership(error_p) 1974 for error_p in raw_error_p 1975 ) 1976 1977 return profile.LinkedProfile( 1978 bungie_user=bungie_user, 1979 profiles=profiles, 1980 profiles_with_errors=error_profiles, 1981 ) 1982 1983 def deserialize_clan_banners( 1984 self, payload: typedefs.JSONObject 1985 ) -> collections.Sequence[clans.ClanBanner]: 1986 if banners := payload.get("clanBannerDecals"): 1987 banner_obj = tuple( 1988 clans.ClanBanner( 1989 id=int(k), 1990 foreground=assets.Image(path=v["foregroundPath"]), 1991 background=assets.Image(path=v["backgroundPath"]), 1992 ) 1993 for k, v in banners.items() 1994 ) 1995 else: 1996 banner_obj = () 1997 return banner_obj 1998 1999 def deserialize_public_milestone_content( 2000 self, payload: typedefs.JSONObject 2001 ) -> milestones.MilestoneContent: 2002 if raw_categories := payload.get("itemCategories"): 2003 items_categories = tuple( 2004 milestones.MilestoneItems( 2005 title=item["title"], hashes=item["itemHashes"] 2006 ) 2007 for item in raw_categories 2008 ) 2009 else: 2010 items_categories = () 2011 2012 return milestones.MilestoneContent( 2013 about=typedefs.unknown(payload["about"]), 2014 status=typedefs.unknown(payload["status"]), 2015 tips=payload.get("tips", ()), 2016 items=items_categories, 2017 ) 2018 2019 def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend: 2020 bungie_user: user.BungieUser | None = None 2021 2022 if raw_bungie_user := payload.get("bungieNetUser"): 2023 bungie_user = self.deserialize_bungie_user(raw_bungie_user) 2024 2025 return friends.Friend( 2026 net=self._net, 2027 id=int(payload["lastSeenAsMembershipId"]), 2028 name=typedefs.unknown(payload["bungieGlobalDisplayName"]), 2029 code=payload.get("bungieGlobalDisplayNameCode"), 2030 relationship=enums.Relationship(payload["relationship"]), 2031 user=bungie_user, 2032 online_status=enums.Presence(payload["onlineStatus"]), 2033 online_title=payload["onlineTitle"], 2034 type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]), 2035 ) 2036 2037 def deserialize_friends( 2038 self, payload: typedefs.JSONObject 2039 ) -> collections.Sequence[friends.Friend]: 2040 return tuple(self.deserialize_friend(friend) for friend in payload["friends"]) 2041 2042 def deserialize_friend_requests( 2043 self, payload: typedefs.JSONObject 2044 ) -> friends.FriendRequestView: 2045 if raw_incoming_requests := payload.get("incomingRequests"): 2046 incoming = tuple( 2047 self.deserialize_friend(incoming_request) 2048 for incoming_request in raw_incoming_requests 2049 ) 2050 else: 2051 incoming = () 2052 2053 if raw_outgoing_requests := payload.get("outgoingRequests"): 2054 outgoing = tuple( 2055 self.deserialize_friend(outgoing_request) 2056 for outgoing_request in raw_outgoing_requests 2057 ) 2058 else: 2059 outgoing = () 2060 2061 return friends.FriendRequestView(incoming=incoming, outgoing=outgoing) 2062 2063 def _set_fireteam_fields( 2064 self, payload: typedefs.JSONObject, total_results: int | None = None 2065 ) -> fireteams.Fireteam: 2066 activity_type = fireteams.FireteamActivity(payload["activityType"]) 2067 return fireteams.Fireteam( 2068 id=int(payload["fireteamId"]), 2069 group_id=int(payload["groupId"]), 2070 platform=fireteams.FireteamPlatform(payload["platform"]), 2071 is_immediate=payload["isImmediate"], 2072 activity_type=activity_type, 2073 owner_id=int(payload["ownerMembershipId"]), 2074 player_slot_count=payload["playerSlotCount"], 2075 available_player_slots=payload["availablePlayerSlotCount"], 2076 available_alternate_slots=payload["availableAlternateSlotCount"], 2077 title=payload["title"], 2078 date_created=time.clean_date(payload["dateCreated"]), 2079 is_public=payload["isPublic"], 2080 locale=fireteams.FireteamLanguage(payload["locale"]), 2081 is_valid=payload["isValid"], 2082 last_modified=time.clean_date(payload["datePlayerModified"]), 2083 date_modified=time.clean_date(payload["dateModified"]) 2084 if "dateModified" in payload 2085 else None, 2086 scheduled_time=time.clean_date(payload["scheduledTime"]) 2087 if "scheduledTime" in payload 2088 else None, 2089 total_results=total_results or 0, 2090 ) 2091 2092 def deserialize_fireteams( 2093 self, payload: typedefs.JSONObject 2094 ) -> collections.Sequence[fireteams.Fireteam]: 2095 if "results" in payload: 2096 fireteams_ = tuple( 2097 self._set_fireteam_fields( 2098 elem, total_results=int(payload["totalResults"]) 2099 ) 2100 for elem in payload["results"] 2101 ) 2102 else: 2103 fireteams_ = () 2104 return fireteams_ 2105 2106 def deserialize_fireteam_destiny_users( 2107 self, payload: typedefs.JSONObject 2108 ) -> fireteams.FireteamUser: 2109 destiny_obj = self.deserialize_destiny_membership(payload) 2110 return fireteams.FireteamUser( 2111 net=self._net, 2112 id=destiny_obj.id, 2113 code=destiny_obj.code, 2114 icon=destiny_obj.icon, 2115 types=destiny_obj.types, 2116 type=destiny_obj.type, 2117 is_public=destiny_obj.is_public, 2118 crossave_override=destiny_obj.crossave_override, 2119 name=destiny_obj.name, 2120 last_seen_name=destiny_obj.last_seen_name, 2121 fireteam_display_name=payload["FireteamDisplayName"], 2122 fireteam_membership_id=enums.MembershipType( 2123 payload["FireteamMembershipType"] 2124 ), 2125 ) 2126 2127 def deserialize_fireteam_members( 2128 self, payload: typedefs.JSONObject, *, alternatives: bool = False 2129 ) -> collections.Sequence[fireteams.FireteamMember]: 2130 members_: list[fireteams.FireteamMember] = [] 2131 if members := payload.get("Members" if not alternatives else "Alternates"): 2132 for member in members: 2133 bungie_fields = self.deserialize_partial_bungie_user(member) 2134 members_fields = fireteams.FireteamMember( 2135 destiny_user=self.deserialize_fireteam_destiny_users(member), 2136 has_microphone=member["hasMicrophone"], 2137 character_id=int(member["characterId"]), 2138 date_joined=time.clean_date(member["dateJoined"]), 2139 last_platform_invite_date=time.clean_date( 2140 member["lastPlatformInviteAttemptDate"] 2141 ), 2142 last_platform_invite_result=int( 2143 member["lastPlatformInviteAttemptResult"] 2144 ), 2145 net=self._net, 2146 name=bungie_fields.name, 2147 id=bungie_fields.id, 2148 icon=bungie_fields.icon, 2149 is_public=bungie_fields.is_public, 2150 crossave_override=bungie_fields.crossave_override, 2151 types=bungie_fields.types, 2152 type=bungie_fields.type, 2153 ) 2154 members_.append(members_fields) 2155 return tuple(members_) 2156 2157 def deserialize_available_fireteam( 2158 self, payload: typedefs.JSONObject 2159 ) -> fireteams.AvailableFireteam: 2160 fields = self._set_fireteam_fields(payload["Summary"]) 2161 return fireteams.AvailableFireteam( 2162 id=fields.id, 2163 group_id=fields.group_id, 2164 platform=fields.platform, 2165 activity_type=fields.activity_type, 2166 is_immediate=fields.is_immediate, 2167 is_public=fields.is_public, 2168 is_valid=fields.is_valid, 2169 owner_id=fields.owner_id, 2170 player_slot_count=fields.player_slot_count, 2171 available_player_slots=fields.available_player_slots, 2172 available_alternate_slots=fields.available_alternate_slots, 2173 title=fields.title, 2174 date_created=fields.date_created, 2175 locale=fields.locale, 2176 last_modified=fields.last_modified, 2177 total_results=fields.total_results, 2178 scheduled_time=fields.scheduled_time, 2179 date_modified=fields.date_modified, 2180 members=self.deserialize_fireteam_members(payload), 2181 alternatives=self.deserialize_fireteam_members(payload, alternatives=True), 2182 ) 2183 2184 def deserialize_available_fireteams( 2185 self, data: typedefs.JSONObject 2186 ) -> collections.Sequence[fireteams.AvailableFireteam]: 2187 if raw_results := data.get("results"): 2188 fireteam_results = tuple( 2189 self.deserialize_available_fireteam(f) for f in raw_results 2190 ) 2191 else: 2192 fireteam_results = () 2193 return fireteam_results 2194 2195 def deserialize_fireteam_party( 2196 self, payload: typedefs.JSONObject 2197 ) -> fireteams.FireteamParty: 2198 last_destination_hash: int | None = None 2199 if raw_dest_hash := payload.get("lastOrbitedDestinationHash"): 2200 last_destination_hash = int(raw_dest_hash) 2201 2202 return fireteams.FireteamParty( 2203 members=tuple( 2204 self._deserialize_fireteam_party_member(member) 2205 for member in payload["partyMembers"] 2206 ), 2207 activity=self._deserialize_fireteam_party_current_activity( 2208 payload["currentActivity"] 2209 ), 2210 settings=self._deserialize_fireteam_party_settings(payload["joinability"]), 2211 last_destination_hash=last_destination_hash, 2212 tracking=payload["tracking"], 2213 ) 2214 2215 def _deserialize_fireteam_party_member( 2216 self, payload: typedefs.JSONObject 2217 ) -> fireteams.FireteamPartyMember: 2218 status = fireteams.FireteamPartyMemberState(payload["status"]) 2219 2220 return fireteams.FireteamPartyMember( 2221 membership_id=int(payload["membershipId"]), 2222 emblem_hash=int(payload["emblemHash"]), 2223 status=status, 2224 display_name=payload["displayName"] if payload["displayName"] else None, 2225 ) 2226 2227 def _deserialize_fireteam_party_current_activity( 2228 self, payload: typedefs.JSONObject 2229 ) -> fireteams.FireteamPartyCurrentActivity: 2230 start_date: datetime.datetime | None = None 2231 if raw_start_date := payload.get("startTime"): 2232 start_date = time.clean_date(raw_start_date) 2233 2234 end_date: datetime.datetime | None = None 2235 if raw_end_date := payload.get("endTime"): 2236 end_date = time.clean_date(raw_end_date) 2237 return fireteams.FireteamPartyCurrentActivity( 2238 start_time=start_date, 2239 end_time=end_date, 2240 score=float(payload["score"]), 2241 highest_opposing_score=float(payload["highestOpposingFactionScore"]), 2242 opponents_count=int(payload["numberOfOpponents"]), 2243 player_count=int(payload["numberOfPlayers"]), 2244 ) 2245 2246 def _deserialize_fireteam_party_settings( 2247 self, payload: typedefs.JSONObject 2248 ) -> fireteams.FireteamPartySettings: 2249 closed_reasons = enums.ClosedReasons(payload["closedReasons"]) 2250 return fireteams.FireteamPartySettings( 2251 open_slots=int(payload["openSlots"]), 2252 privacy_setting=enums.PrivacySetting(int(payload["privacySetting"])), 2253 closed_reasons=closed_reasons, 2254 ) 2255 2256 def deserialize_seasonal_artifact( 2257 self, payload: typedefs.JSONObject 2258 ) -> season.Artifact: 2259 raw_artifact = payload["seasonalArtifact"] 2260 2261 points = raw_artifact["pointProgression"] 2262 points_prog = progressions.Progression( 2263 hash=points["progressionHash"], 2264 level=points["level"], 2265 cap=points["levelCap"], 2266 daily_limit=points["dailyLimit"], 2267 weekly_limit=points["weeklyLimit"], 2268 current_progress=points["currentProgress"], 2269 daily_progress=points["dailyProgress"], 2270 needed=points["progressToNextLevel"], 2271 next_level=points["nextLevelAt"], 2272 ) 2273 2274 bonus = raw_artifact["powerBonusProgression"] 2275 power_bonus_prog = progressions.Progression( 2276 hash=bonus["progressionHash"], 2277 level=bonus["level"], 2278 cap=bonus["levelCap"], 2279 daily_limit=bonus["dailyLimit"], 2280 weekly_limit=bonus["weeklyLimit"], 2281 current_progress=bonus["currentProgress"], 2282 daily_progress=bonus["dailyProgress"], 2283 needed=bonus["progressToNextLevel"], 2284 next_level=bonus["nextLevelAt"], 2285 ) 2286 return season.Artifact( 2287 hash=raw_artifact["artifactHash"], 2288 power_bonus=raw_artifact["powerBonus"], 2289 acquired_points=raw_artifact["pointsAcquired"], 2290 bonus=power_bonus_prog, 2291 points=points_prog, 2292 ) 2293 2294 def deserialize_profile_progression( 2295 self, payload: typedefs.JSONObject 2296 ) -> profile.ProfileProgression: 2297 return profile.ProfileProgression( 2298 artifact=self.deserialize_seasonal_artifact(payload["data"]), 2299 checklist={ 2300 int(check_id): checklists 2301 for check_id, checklists in payload["data"]["checklists"].items() 2302 }, 2303 ) 2304 2305 def deserialize_instanced_item( 2306 self, payload: typedefs.JSONObject 2307 ) -> items.ItemInstance: 2308 damage_type_hash: int | None = None 2309 if raw_damagetype_hash := payload.get("damageTypeHash"): 2310 damage_type_hash = int(raw_damagetype_hash) 2311 2312 required_hashes: typing.Optional[collections.Collection[int]] = None 2313 if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"): 2314 required_hashes = tuple(int(raw_hash) for raw_hash in raw_required_hashes) 2315 2316 breaker_type: items.ItemBreakerType | None = None 2317 if raw_break_type := payload.get("breakerType"): 2318 breaker_type = items.ItemBreakerType(int(raw_break_type)) 2319 2320 breaker_type_hash: int | None = None 2321 if raw_break_type_hash := payload.get("breakerTypeHash"): 2322 breaker_type_hash = int(raw_break_type_hash) 2323 2324 energy: items.ItemEnergy | None = None 2325 if raw_energy := payload.get("energy"): 2326 energy = self.deserialize_item_energy(raw_energy) 2327 2328 primary_stats = None 2329 if raw_primary_stats := payload.get("primaryStat"): 2330 primary_stats = self.deserialize_item_stats_view(raw_primary_stats) 2331 2332 return items.ItemInstance( 2333 damage_type=enums.DamageType(int(payload["damageType"])), 2334 damage_type_hash=damage_type_hash, 2335 primary_stat=primary_stats, 2336 item_level=int(payload["itemLevel"]), 2337 quality=int(payload["quality"]), 2338 is_equipped=payload["isEquipped"], 2339 can_equip=payload["canEquip"], 2340 equip_required_level=int(payload["equipRequiredLevel"]), 2341 required_equip_unlock_hashes=required_hashes, 2342 cant_equip_reason=int(payload["cannotEquipReason"]), 2343 breaker_type=breaker_type, 2344 breaker_type_hash=breaker_type_hash, 2345 energy=energy, 2346 ) 2347 2348 def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy: 2349 energy_hash: int | None = None 2350 if raw_energy_hash := payload.get("energyTypeHash"): 2351 energy_hash = int(raw_energy_hash) 2352 2353 return items.ItemEnergy( 2354 hash=energy_hash, 2355 type=items.ItemEnergyType(int(payload["energyType"])), 2356 capacity=int(payload["energyCapacity"]), 2357 used_energy=int(payload["energyUsed"]), 2358 unused_energy=int(payload["energyUnused"]), 2359 ) 2360 2361 def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk: 2362 perk_hash: int | None = None 2363 if raw_perk_hash := payload.get("perkHash"): 2364 perk_hash = int(raw_perk_hash) 2365 2366 return items.ItemPerk( 2367 hash=perk_hash, 2368 icon=assets.Image(path=payload["iconPath"]), 2369 is_active=payload["isActive"], 2370 is_visible=payload["visible"], 2371 ) 2372 2373 def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket: 2374 plug_hash: int | None = None 2375 if raw_plug_hash := payload.get("plugHash"): 2376 plug_hash = int(raw_plug_hash) 2377 2378 enable_fail_indexes: collections.Sequence[int] | None = None 2379 if raw_indexes := payload.get("enableFailIndexes"): 2380 enable_fail_indexes = tuple(int(index) for index in raw_indexes) 2381 2382 return items.ItemSocket( 2383 plug_hash=plug_hash, 2384 is_enabled=payload["isEnabled"], 2385 enable_fail_indexes=enable_fail_indexes, 2386 is_visible=payload.get("visible"), 2387 ) 2388 2389 def deserialize_item_stats_view( 2390 self, payload: typedefs.JSONObject 2391 ) -> items.ItemStatsView: 2392 return items.ItemStatsView( 2393 stat_hash=payload.get("statHash"), value=payload.get("value") 2394 ) 2395 2396 def deserialize_plug_item_state( 2397 self, payload: typedefs.JSONObject 2398 ) -> items.PlugItemState: 2399 item_hash: int | None = None 2400 if raw_item_hash := payload.get("plugItemHash"): 2401 item_hash = int(raw_item_hash) 2402 2403 insert_fail_indexes: collections.Sequence[int] | None = None 2404 if raw_fail_indexes := payload.get("insertFailIndexes"): 2405 insert_fail_indexes = tuple(int(k) for k in raw_fail_indexes) 2406 2407 enable_fail_indexes: collections.Sequence[int] | None = None 2408 if raw_enabled_indexes := payload.get("enableFailIndexes"): 2409 enable_fail_indexes = tuple(int(k) for k in raw_enabled_indexes) 2410 2411 return items.PlugItemState( 2412 item_hash=item_hash, 2413 insert_fail_indexes=insert_fail_indexes, 2414 enable_fail_indexes=enable_fail_indexes, 2415 is_enabled=payload["enabled"], 2416 can_insert=payload["canInsert"], 2417 )
The base deserialization factory class for all aiobungie objects.
This entity factory is used to deserialize JSON responses from the REST client and turning them
into a aiobungie.crates Python classes.
72 def deserialize_bungie_user(self, data: typedefs.JSONObject) -> user.BungieUser: 73 return user.BungieUser( 74 id=int(data["membershipId"]), 75 created_at=time.clean_date(data["firstAccess"]), 76 name=data.get("cachedBungieGlobalDisplayName"), 77 is_deleted=data["isDeleted"], 78 about=data["about"], 79 updated_at=time.clean_date(data["lastUpdate"]), 80 psn_name=data.get("psnDisplayName", None), 81 stadia_name=data.get("stadiaDisplayName", None), 82 steam_name=data.get("steamDisplayName", None), 83 twitch_name=data.get("twitchDisplayName", None), 84 blizzard_name=data.get("blizzardDisplayName", None), 85 status=data["statusText"], 86 locale=data["locale"], 87 picture=assets.Image(path=data["profilePicturePath"]), 88 code=data.get("cachedBungieGlobalDisplayNameCode", None), 89 unique_name=data.get("uniqueName", None), 90 theme_id=int(data["profileTheme"]), 91 show_activity=bool(data["showActivity"]), 92 theme_name=data["profileThemeName"], 93 display_title=data["userTitleDisplay"], 94 )
Deserialize a raw JSON Bungie.net user only payload into a user object.
This only returns the Bungie.net user and not the Destiny memberships.
Parameters
- data (
aiobungie.typedefs.JSONObject): The JSON data/payload.
Returns
aiobungie.crates.BungieUser: A Bungie user.
96 def deserialize_partial_bungie_user( 97 self, payload: typedefs.JSONObject 98 ) -> user.PartialBungieUser: 99 return user.PartialBungieUser( 100 net=self._net, 101 types=tuple( 102 enums.MembershipType(type_) 103 for type_ in payload.get("applicableMembershipTypes", ()) 104 ), 105 name=payload.get("displayName"), 106 id=int(payload["membershipId"]), 107 crossave_override=enums.MembershipType(payload["crossSaveOverride"]), 108 is_public=payload["isPublic"], 109 icon=assets.Image(path=payload.get("iconPath", "")), 110 type=enums.MembershipType(payload["membershipType"]), 111 )
Deserialize a raw JSON of a partial bungieNetUserInfo.
A partial user is a bungie.net user payload with missing information from
the main BungieUser object.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.PartialBungieUser: A partial bungie user.
113 def deserialize_destiny_membership( 114 self, payload: typedefs.JSONObject 115 ) -> user.DestinyMembership: 116 name: str | None = None 117 if (raw_name := payload.get("bungieGlobalDisplayName")) is not None: 118 name = typedefs.unknown(raw_name) 119 120 return user.DestinyMembership( 121 net=self._net, 122 id=int(payload["membershipId"]), 123 name=name, 124 code=payload.get("bungieGlobalDisplayNameCode", None), 125 last_seen_name=payload.get("LastSeenDisplayName") 126 or payload.get("displayName") # noqa: W503 127 or "", # noqa: W503 128 type=enums.MembershipType(payload["membershipType"]), 129 is_public=payload["isPublic"], 130 crossave_override=enums.MembershipType(payload["crossSaveOverride"]), 131 icon=assets.Image(path=payload.get("iconPath", "")), 132 types=tuple( 133 enums.MembershipType(type_) 134 for type_ in payload.get("applicableMembershipTypes", ()) 135 ), 136 )
Deserialize a raw JSON of destinyUserInfo destiny membership information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.user.DestinyMembership: A Destiny 2 membership.
138 def deserialize_destiny_memberships( 139 self, data: typedefs.JSONArray 140 ) -> collections.Sequence[user.DestinyMembership]: 141 return tuple( 142 self.deserialize_destiny_membership(membership) for membership in data 143 )
Deserialize a raw JSON payload/array of destinyUserInfo.
Parameters
- payload (
aiobungie.typedefs.JSONArray): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.user.DestinyMembership]: A sequence of Destiny 2 memberships.
145 def deserialize_user(self, data: typedefs.JSONObject) -> user.User: 146 primary_membership_id: int | None = None 147 if raw_primary_id := data.get("primaryMembershipId"): 148 primary_membership_id = int(raw_primary_id) 149 150 return user.User( 151 bungie_user=self.deserialize_bungie_user(data["bungieNetUser"]), 152 memberships=self.deserialize_destiny_memberships( 153 data["destinyMemberships"] 154 ), 155 primary_membership_id=primary_membership_id, 156 )
Deserialize a raw JSON results of fetched user memberships and Bungie.net user its their id.
Parameters
- data (
aiobungie.typedefs.JSONObject): The JSON data/payload.
Returns
aiobungie.crates.User: A user object.
158 def deserialize_searched_user( 159 self, payload: typedefs.JSONObject 160 ) -> user.SearchableDestinyUser: 161 code: int | None = None 162 if raw_code := payload.get("bungieGlobalDisplayNameCode"): 163 code = int(raw_code) 164 165 bungie_id: int | None = None 166 if raw_bungie_id := payload.get("bungieNetMembershipId"): 167 bungie_id = int(raw_bungie_id) 168 169 return user.SearchableDestinyUser( 170 name=typedefs.unknown(payload["bungieGlobalDisplayName"]), 171 code=code, 172 bungie_id=bungie_id, 173 memberships=self.deserialize_destiny_memberships( 174 payload["destinyMemberships"] 175 ), 176 )
Deserialize the results of user search details.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.SearchableDestinyUser: The searched for Destiny 2 membership.
178 def deserialize_user_credentials( 179 self, payload: typedefs.JSONArray 180 ) -> collections.Sequence[user.UserCredentials]: 181 return tuple( 182 user.UserCredentials( 183 type=enums.CredentialType(int(creds["credentialType"])), 184 display_name=creds["credentialDisplayName"], 185 is_public=creds["isPublic"], 186 self_as_string=creds.get("credentialAsString"), 187 ) 188 for creds in payload 189 )
Deserialize a JSON array of Bungie user credentials.
Parameters
- payload (
aiobungie.typedefs.JSONArray): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.UserCredentials]: A sequence of user's credentials.
191 def deserialize_user_themes( 192 self, payload: typedefs.JSONArray 193 ) -> collections.Sequence[user.UserThemes]: 194 return tuple( 195 user.UserThemes( 196 id=int(entry["userThemeId"]), 197 name=entry["userThemeName"] if "userThemeName" in entry else None, 198 description=entry["userThemeDescription"] 199 if "userThemeDescription" in entry 200 else None, 201 ) 202 for entry in payload 203 )
Deserialize a raw JSON array of Bungie user themes.
Parameters
- payload (
aiobungie.typedefs.JSONArray): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.user.UserThemes]: A sequence of bungie user themes.
254 def deserialize_clan(self, payload: typedefs.JSONObject) -> clans.Clan: 255 current_user_map: collections.Mapping[str, clans.ClanMember] | None = None 256 if raw_current_user := payload.get("currentUserMemberMap"): 257 # This will get populated if only it was a GroupsV2.GroupResponse. 258 # GroupsV2.GetGroupsForMemberResponse doesn't have this field. 259 current_user_map = { 260 membership_type: self.deserialize_clan_member(membership) 261 for membership_type, membership in raw_current_user.items() 262 } 263 264 return self._deserialize_group_details( 265 data=payload["detail"], 266 clan_founder=self.deserialize_clan_member(payload["founder"]), 267 current_user_memberships=current_user_map, 268 )
Deserialize a raw JSON payload of Bungie clan information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.Clan: A clan owner.
270 def deserialize_clan_member(self, data: typedefs.JSONObject, /) -> clans.ClanMember: 271 destiny_user = self.deserialize_destiny_membership(data["destinyUserInfo"]) 272 return clans.ClanMember( 273 net=self._net, 274 last_seen_name=destiny_user.last_seen_name, 275 id=destiny_user.id, 276 name=destiny_user.name, 277 icon=destiny_user.icon, 278 last_online=time.from_timestamp(int(data["lastOnlineStatusChange"])), 279 group_id=int(data["groupId"]), 280 joined_at=time.clean_date(data["joinDate"]), 281 types=destiny_user.types, 282 is_public=destiny_user.is_public, 283 type=destiny_user.type, 284 code=destiny_user.code, 285 is_online=data["isOnline"], 286 crossave_override=destiny_user.crossave_override, 287 bungie_user=self.deserialize_partial_bungie_user(data["bungieNetUserInfo"]) 288 if "bungieNetUserInfo" in data 289 else None, 290 member_type=enums.ClanMemberType(int(data["memberType"])), 291 )
Deserialize a JSON payload of a clan member information.
Parameters
- data (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.ClanMember: A clan member.
293 def deserialize_clan_members( 294 self, data: typedefs.JSONObject, / 295 ) -> iterators.Iterator[clans.ClanMember]: 296 return iterators.Iterator( 297 self.deserialize_clan_member(member) for member in data["results"] 298 )
Deserialize a JSON payload of a clan members information.
Parameters
- data (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.Iterator[aiobungie.crates.ClanMember]: An iterator of clan members of the deserialized payload.
300 def deserialize_group_member( 301 self, payload: typedefs.JSONObject 302 ) -> clans.GroupMember: 303 member = payload["member"] 304 return clans.GroupMember( 305 net=self._net, 306 join_date=time.clean_date(member["joinDate"]), 307 group_id=int(member["groupId"]), 308 member_type=enums.ClanMemberType(member["memberType"]), 309 is_online=member["isOnline"], 310 last_online=time.from_timestamp(int(member["lastOnlineStatusChange"])), 311 inactive_memberships=payload.get("areAllMembershipsInactive", None), 312 member=self.deserialize_destiny_membership(member["destinyUserInfo"]), 313 group=self._deserialize_group_details(payload["group"]), 314 )
Deserialize a JSON payload of group information for a member.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.GroupMember: A group member object.
328 def deserialize_clan_conversations( 329 self, payload: typedefs.JSONArray 330 ) -> collections.Sequence[clans.ClanConversation]: 331 return tuple(self._deserialize_clan_conversation(conv) for conv in payload)
Deserialize a JSON array of a clan conversations information.
Parameters
- payload (
aiobungie.typedefs.JSONArray): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.ClanConversation]: A sequence of clan conversations of the deserialized payload.
333 def deserialize_app_owner( 334 self, payload: typedefs.JSONObject 335 ) -> application.ApplicationOwner: 336 return application.ApplicationOwner( 337 net=self._net, 338 name=payload.get("bungieGlobalDisplayName"), 339 id=int(payload["membershipId"]), 340 type=enums.MembershipType(payload["membershipType"]), 341 icon=assets.Image(path=payload["iconPath"]), 342 is_public=payload["isPublic"], 343 code=payload.get("bungieGlobalDisplayNameCode", None), 344 )
Deserialize a JSON payload of Bungie Developer portal application owner information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.application.ApplicationOwner: An application owner.
346 def deserialize_app(self, payload: typedefs.JSONObject) -> application.Application: 347 return application.Application( 348 id=int(payload["applicationId"]), 349 name=payload["name"], 350 link=payload["link"], 351 status=payload["status"], 352 redirect_url=payload.get("redirectUrl", None), 353 created_at=time.clean_date(payload["creationDate"]), 354 published_at=time.clean_date(payload["firstPublished"]), 355 owner=self.deserialize_app_owner(payload["team"][0]["user"]), 356 scope=payload.get("scope"), 357 )
Deserialize a JSON payload of Bungie Developer portal application information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.application.Application: An application.
383 def deserialize_profile(self, payload: typedefs.JSONObject, /) -> profile.Profile: 384 payload = payload["data"] 385 id = int(payload["userInfo"]["membershipId"]) 386 name = payload["userInfo"]["displayName"] 387 is_public = payload["userInfo"]["isPublic"] 388 type = enums.MembershipType(payload["userInfo"]["membershipType"]) 389 last_played = time.clean_date(payload["dateLastPlayed"]) 390 character_ids = tuple(int(cid) for cid in payload["characterIds"]) 391 power_cap = payload["currentSeasonRewardPowerCap"] 392 393 return profile.Profile( 394 id=int(id), 395 name=name, 396 is_public=is_public, 397 type=type, 398 last_played=last_played, 399 character_ids=character_ids, 400 power_cap=power_cap, 401 net=self._net, 402 )
Deserialize a JSON payload of Bungie.net profile information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.Profile: A profile object of the deserialized payload.
404 def deserialize_profile_item( 405 self, payload: typedefs.JSONObject 406 ) -> profile.ProfileItemImpl: 407 instance_id: int | None = None 408 if raw_instance_id := payload.get("itemInstanceId"): 409 instance_id = int(raw_instance_id) 410 411 version_number: int | None = None 412 if raw_version := payload.get("versionNumber"): 413 version_number = int(raw_version) 414 415 transfer_status = enums.TransferStatus(payload["transferStatus"]) 416 417 return profile.ProfileItemImpl( 418 net=self._net, 419 hash=payload["itemHash"], 420 quantity=payload["quantity"], 421 bind_status=enums.ItemBindStatus(payload["bindStatus"]), 422 location=enums.ItemLocation(payload["location"]), 423 bucket=payload["bucketHash"], 424 transfer_status=transfer_status, 425 lockable=payload["lockable"], 426 state=enums.ItemState(payload["state"]), 427 dismantle_permissions=payload["dismantlePermission"], 428 is_wrapper=payload["isWrapper"], 429 instance_id=instance_id, 430 version_number=version_number, 431 ornament_id=payload.get("overrideStyleItemHash"), 432 )
Deserialize a JSON payload of a singular profile component item.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.ProfileItemImpl: Implementation of a Destiny 2 profile component item.
434 def deserialize_objectives(self, payload: typedefs.JSONObject) -> records.Objective: 435 return records.Objective( 436 net=self._net, 437 hash=payload["objectiveHash"], 438 visible=payload["visible"], 439 complete=payload["complete"], 440 completion_value=payload["completionValue"], 441 progress=payload.get("progress"), 442 destination_hash=payload.get("destinationHash"), 443 activity_hash=payload.get("activityHash"), 444 )
Deserialize a JSON payload of an objective found in a record profile component.
Parameters
- payload (
aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
aiobungie.crates.records.Objective: A record objective object.
447 def deserialize_records( 448 self, 449 payload: typedefs.JSONObject, 450 scores: records.RecordScores | None = None, 451 **nodes: int, 452 ) -> records.Record: 453 objectives: collections.Sequence[records.Objective] | None = None 454 interval_objectives: collections.Sequence[records.Objective] | None = None 455 record_state: records.RecordState | int 456 457 record_state = records.RecordState(payload["state"]) 458 459 if raw_objs := payload.get("objectives"): 460 objectives = tuple(self.deserialize_objectives(obj) for obj in raw_objs) 461 462 if raw_interval_objs := payload.get("intervalObjectives"): 463 interval_objectives = tuple( 464 self.deserialize_objectives(obj) for obj in raw_interval_objs 465 ) 466 467 return records.Record( 468 scores=scores, 469 categories_node_hash=nodes.get("categories_hash"), 470 seals_node_hash=nodes.get("seals_hash"), 471 state=record_state, 472 objectives=objectives, 473 interval_objectives=interval_objectives, 474 redeemed_count=payload.get("intervalsRedeemedCount", 0), 475 completion_times=payload.get("completedCount", None), 476 reward_visibility=payload.get("rewardVisibility"), 477 )
Deserialize a JSON object of a profile record component.
Parameters
- payload (
aiobungie.internal.helpers.JsonObject): The JSON object payload - scores (
records.RecordScores | None): The records scores object. This exists only to keep the signature ofaiobungie.crates.CharacterRecordwith the record object. As it will always beNonein that object. - **nodes (
int): An int kwargs use to grab the node hashes while deserializing components.
Returns
aiobungie.records.Record: A standard implementation of a profile record component.
479 def deserialize_character_records( 480 self, 481 payload: typedefs.JSONObject, 482 scores: records.RecordScores | None = None, 483 record_hashes: collections.Sequence[int] = (), 484 ) -> records.CharacterRecord: 485 record = self.deserialize_records(payload, scores) 486 return records.CharacterRecord( 487 scores=scores, 488 categories_node_hash=record.categories_node_hash, 489 seals_node_hash=record.seals_node_hash, 490 state=record.state, 491 objectives=record.objectives, 492 interval_objectives=record.interval_objectives, 493 redeemed_count=payload.get("intervalsRedeemedCount", 0), 494 completion_times=payload.get("completedCount"), 495 reward_visibility=payload.get("rewardVisibility"), 496 record_hashes=record_hashes, 497 )
Deserialize a JSON object of a profile character record component.
This almost does the same this as deserialize_records but
has more fields which can only be found in a character record.
Parameters
- payload (
aiobungie.internal.helpers.JsonObject): The JSON object payload
Returns
aiobungie.records.CharacterRecord: A standard implementation of a profile character record component.
499 def deserialize_character_dye(self, payload: typedefs.JSONObject) -> character.Dye: 500 return character.Dye( 501 channel_hash=payload["channelHash"], dye_hash=payload["dyeHash"] 502 )
Deserialize a JSON payload of a character's dye information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.character.Dye: Information about a character dye object.
504 def deserialize_character_customization( 505 self, payload: typedefs.JSONObject 506 ) -> character.CustomizationOptions: 507 return character.CustomizationOptions( 508 personality=payload["personality"], 509 face=payload["face"], 510 skin_color=payload["skinColor"], 511 lip_color=payload["lipColor"], 512 eye_color=payload["eyeColor"], 513 hair_colors=payload.get("hairColors", ()), 514 feature_colors=payload.get("featureColors", ()), 515 decal_color=payload["decalColor"], 516 wear_helmet=payload["wearHelmet"], 517 hair_index=payload["hairIndex"], 518 feature_index=payload["featureIndex"], 519 decal_index=payload["decalIndex"], 520 )
Deserialize a JSON payload of a character customization information found in character render data profile component.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.character.CustomizationOptions: Information about a character customs object.
522 def deserialize_character_minimal_equipments( 523 self, payload: typedefs.JSONObject 524 ) -> character.MinimalEquipments: 525 if raw_dyes := payload.get("dyes"): 526 dyes = tuple(self.deserialize_character_dye(dye) for dye in raw_dyes) 527 else: 528 dyes = () 529 530 return character.MinimalEquipments( 531 net=self._net, item_hash=payload["itemHash"], dyes=dyes 532 )
Deserialize a singular JSON peer view of equipment found in character render data profile component.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.character.MinimalEquipments: A minimal equipment object.
534 def deserialize_character_render_data( 535 self, payload: typedefs.JSONObject, / 536 ) -> character.RenderedData: 537 return character.RenderedData( 538 net=self._net, 539 customization=self.deserialize_character_customization( 540 payload["customization"] 541 ), 542 custom_dyes=tuple( 543 self.deserialize_character_dye(dye) 544 for dye in payload["customDyes"] 545 if dye 546 ), 547 equipment=tuple( 548 self.deserialize_character_minimal_equipments(equipment) 549 for equipment in payload["peerView"]["equipment"] 550 ), 551 )
Deserialize a JSON payload of a profile character render data component.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.RenderedData: A character rendered data profile component.
553 def deserialize_available_activity( 554 self, payload: typedefs.JSONObject 555 ) -> activity.AvailableActivity: 556 return activity.AvailableActivity( 557 hash=payload["activityHash"], 558 is_new=payload["isNew"], 559 is_completed=payload["isCompleted"], 560 is_visible=payload["isVisible"], 561 display_level=payload.get("displayLevel"), 562 recommended_light=payload.get("recommendedLight"), 563 difficulty=activity.Difficulty(payload["difficultyTier"]), 564 can_join=payload["canJoin"], 565 can_lead=payload["canLead"], 566 )
Deserialize a JSON payload of an available activities.
This method is used to deserialize an array of aiobungie.crates.CharacterActivity.available_activities.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.AvailableActivity: An available activity object.
568 def deserialize_character_activity( 569 self, payload: typedefs.JSONObject 570 ) -> activity.CharacterActivity: 571 current_mode: enums.GameMode | None = None 572 if raw_current_mode := payload.get("currentActivityModeType"): 573 current_mode = enums.GameMode(raw_current_mode) 574 575 if raw_current_modes := payload.get("currentActivityModeTypes"): 576 current_mode_types = tuple( 577 enums.GameMode(type_) for type_ in raw_current_modes 578 ) 579 else: 580 current_mode_types = () 581 582 return activity.CharacterActivity( 583 date_started=time.clean_date(payload["dateActivityStarted"]), 584 current_hash=payload["currentActivityHash"], 585 current_mode_hash=payload["currentActivityModeHash"], 586 current_mode=current_mode, 587 current_mode_hashes=payload.get("currentActivityModeHashes", ()), 588 current_mode_types=current_mode_types, 589 current_playlist_hash=payload.get("currentPlaylistActivityHash"), 590 last_story_hash=payload["lastCompletedStoryHash"], 591 available_activities=tuple( 592 self.deserialize_available_activity(activity_) 593 for activity_ in payload["availableActivities"] 594 ), 595 )
Deserialize a JSON payload of character activity profile component.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.CharacterActivity: A character activities component object.
597 def deserialize_profile_items( 598 self, payload: typedefs.JSONObject, / 599 ) -> collections.Sequence[profile.ProfileItemImpl]: 600 return tuple(self.deserialize_profile_item(item) for item in payload["items"])
Deserialize a JSON payload of profile items component information.
This may deserialize profileInventories or profileCurrencies or any
other alternatives.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.ProfileItemImpl]: A profile component object that contains items of the deserialized payload.
643 def deserialize_progressions( 644 self, payload: typedefs.JSONObject 645 ) -> progressions.Progression: 646 return progressions.Progression( 647 hash=int(payload["progressionHash"]), 648 level=int(payload["level"]), 649 cap=int(payload["levelCap"]), 650 daily_limit=int(payload["dailyLimit"]), 651 weekly_limit=int(payload["weeklyLimit"]), 652 current_progress=int(payload["currentProgress"]), 653 daily_progress=int(payload["dailyProgress"]), 654 needed=int(payload["progressToNextLevel"]), 655 next_level=int(payload["nextLevelAt"]), 656 )
741 def deserialize_milestone( 742 self, payload: typedefs.JSONObject 743 ) -> milestones.Milestone: 744 start_date: datetime.datetime | None = None 745 if raw_start_date := payload.get("startDate"): 746 start_date = time.clean_date(raw_start_date) 747 748 end_date: datetime.datetime | None = None 749 if raw_end_date := payload.get("endDate"): 750 end_date = time.clean_date(raw_end_date) 751 752 rewards: collections.Collection[milestones.MilestoneReward] | None = None 753 if raw_rewards := payload.get("rewards"): 754 rewards = tuple( 755 self._deserialize_milestone_rewards(reward) for reward in raw_rewards 756 ) 757 758 activities: collections.Sequence[milestones.MilestoneActivity] | None = None 759 if raw_activities := payload.get("activities"): 760 activities = tuple( 761 self._deserialize_milestone_activity(active) 762 for active in raw_activities 763 ) 764 765 quests: collections.Sequence[milestones.MilestoneQuest] | None = None 766 if raw_quests := payload.get("availableQuests"): 767 quests = tuple( 768 self._deserialize_milestone_available_quest(quest) 769 for quest in raw_quests 770 ) 771 772 vendors: collections.Sequence[milestones.MilestoneVendor] | None = None 773 if raw_vendors := payload.get("vendors"): 774 vendors = tuple( 775 milestones.MilestoneVendor( 776 vendor_hash=vendor["vendorHash"], 777 preview_itemhash=vendor.get("previewItemHash"), 778 ) 779 for vendor in raw_vendors 780 ) 781 782 return milestones.Milestone( 783 hash=payload["milestoneHash"], 784 start_date=start_date, 785 end_date=end_date, 786 order=payload["order"], 787 rewards=rewards, 788 available_quests=quests, 789 activities=activities, 790 vendors=vendors, 791 )
845 def deserialize_character_progressions( 846 self, payload: typedefs.JSONObject 847 ) -> character.CharacterProgression: 848 progressions_ = { 849 int(prog_id): self.deserialize_progressions(prog) 850 for prog_id, prog in payload["progressions"].items() 851 } 852 853 factions = { 854 int(faction_id): self._deserialize_factions(faction) 855 for faction_id, faction in payload["factions"].items() 856 } 857 858 milestones_ = { 859 int(milestone_hash): self.deserialize_milestone(milestone) 860 for milestone_hash, milestone in payload["milestones"].items() 861 } 862 863 uninstanced_item_objectives = { 864 int(item_hash): [self.deserialize_objectives(ins) for ins in obj] 865 for item_hash, obj in payload["uninstancedItemObjectives"].items() 866 } 867 868 artifact = payload["seasonalArtifact"] 869 seasonal_artifact = season.CharacterScopedArtifact( 870 hash=artifact["artifactHash"], 871 points_used=artifact["pointsUsed"], 872 reset_count=artifact["resetCount"], 873 tiers=tuple( 874 self._deserialize_artifact_tiers(tier) for tier in artifact["tiers"] 875 ), 876 ) 877 checklists = payload["checklists"] 878 879 return character.CharacterProgression( 880 progressions=progressions_, 881 factions=factions, 882 checklists=checklists, 883 milestones=milestones_, 884 seasonal_artifact=seasonal_artifact, 885 uninstanced_item_objectives=uninstanced_item_objectives, 886 )
889 def deserialize_character_progressions_mapping(self, payload: typedefs.JSONObject) -> collections.Mapping[int, character.CharacterProgression]: 890 character_progressions: collections.MutableMapping[int, character.CharacterProgression] = {} 891 for char_id, data in payload["data"].items(): 892 character_progressions[int(char_id)] = self.deserialize_character_progressions(data) 893 return character_progressions
896 def deserialize_characters_records( 897 self, 898 payload: typedefs.JSONObject, 899 ) -> collections.Mapping[int, records.CharacterRecord]: 900 return { 901 int(rec_id): self.deserialize_character_records( 902 rec, record_hashes=payload.get("featuredRecordHashes", ()) 903 ) 904 for rec_id, rec in payload["records"].items() 905 }
907 def deserialize_profile_records( 908 self, payload: typedefs.JSONObject 909 ) -> collections.Mapping[int, records.Record]: 910 raw_profile_records = payload["data"] 911 scores = records.RecordScores( 912 current_score=raw_profile_records["score"], 913 legacy_score=raw_profile_records["legacyScore"], 914 lifetime_score=raw_profile_records["lifetimeScore"], 915 ) 916 return { 917 int(record_id): self.deserialize_records( 918 record, 919 scores, 920 categories_hash=raw_profile_records["recordCategoriesRootNodeHash"], 921 seals_hash=raw_profile_records["recordSealsRootNodeHash"], 922 ) 923 for record_id, record in raw_profile_records["records"].items() 924 }
960 def deserialize_craftables_component( 961 self, payload: typedefs.JSONObject 962 ) -> components.CraftablesComponent: 963 return components.CraftablesComponent( 964 net=self._net, 965 craftables={ 966 int(item_id): self._deserialize_craftable_item(item) 967 for item_id, item in payload["craftables"].items() 968 if item is not None 969 }, 970 crafting_root_node_hash=payload["craftingRootNodeHash"], 971 )
973 def deserialize_components( # noqa: C901 Too complex. 974 self, payload: typedefs.JSONObject 975 ) -> components.Component: 976 # Due to how complex this method is, We'll stick to 977 # typing.Optional here. 978 979 profile_: profile.Profile | None = None 980 if raw_profile := payload.get("profile"): 981 profile_ = self.deserialize_profile(raw_profile) 982 983 profile_progression: profile.ProfileProgression | None = None 984 if raw_profile_progression := payload.get("profileProgression"): 985 profile_progression = self.deserialize_profile_progression( 986 raw_profile_progression 987 ) 988 989 profile_currencies: typing.Optional[ 990 collections.Sequence[profile.ProfileItemImpl] 991 ] = None 992 if raw_profile_currencies := payload.get("profileCurrencies"): 993 if "data" in raw_profile_currencies: 994 profile_currencies = self.deserialize_profile_items( 995 raw_profile_currencies["data"] 996 ) 997 998 profile_inventories: typing.Optional[ 999 collections.Sequence[profile.ProfileItemImpl] 1000 ] = None 1001 if raw_profile_inventories := payload.get("profileInventory"): 1002 if "data" in raw_profile_inventories: 1003 profile_inventories = self.deserialize_profile_items( 1004 raw_profile_inventories["data"] 1005 ) 1006 1007 profile_records: typing.Optional[collections.Mapping[int, records.Record]] = ( 1008 None 1009 ) 1010 1011 if raw_profile_records_ := payload.get("profileRecords"): 1012 profile_records = self.deserialize_profile_records(raw_profile_records_) 1013 1014 characters: typing.Optional[collections.Mapping[int, character.Character]] = ( 1015 None 1016 ) 1017 if raw_characters := payload.get("characters"): 1018 characters = self.deserialize_characters(raw_characters) 1019 1020 character_records: typing.Optional[ 1021 collections.Mapping[int, records.CharacterRecord] 1022 ] = None 1023 1024 if raw_character_records := payload.get("characterRecords"): 1025 # Had to do it in two steps.. 1026 to_update = {} 1027 for _, data in raw_character_records["data"].items(): 1028 for record_id, record in data.items(): 1029 to_update[record_id] = record 1030 1031 character_records = { 1032 int(rec_id): self.deserialize_character_records( 1033 rec, record_hashes=to_update.get("featuredRecordHashes", ()) 1034 ) 1035 for rec_id, rec in to_update["records"].items() 1036 } 1037 1038 character_equipments: typing.Optional[ 1039 collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]] 1040 ] = None 1041 if raw_character_equips := payload.get("characterEquipment"): 1042 character_equipments = self.deserialize_character_equipments( 1043 raw_character_equips 1044 ) 1045 1046 character_inventories: typing.Optional[ 1047 collections.Mapping[int, collections.Sequence[profile.ProfileItemImpl]] 1048 ] = None 1049 if raw_character_inventories := payload.get("characterInventories"): 1050 if "data" in raw_character_inventories: 1051 character_inventories = self.deserialize_character_equipments( 1052 raw_character_inventories 1053 ) 1054 1055 character_activities: typing.Optional[ 1056 collections.Mapping[int, activity.CharacterActivity] 1057 ] = None 1058 if raw_char_acts := payload.get("characterActivities"): 1059 character_activities = self.deserialize_character_activities(raw_char_acts) 1060 1061 character_render_data: typing.Optional[ 1062 collections.Mapping[int, character.RenderedData] 1063 ] = None 1064 if raw_character_render_data := payload.get("characterRenderData"): 1065 character_render_data = self.deserialize_characters_render_data( 1066 raw_character_render_data 1067 ) 1068 1069 character_progressions: typing.Optional[ 1070 collections.Mapping[int, character.CharacterProgression] 1071 ] = None 1072 1073 if raw_character_progressions := payload.get("characterProgressions"): 1074 character_progressions = self.deserialize_character_progressions_mapping( 1075 raw_character_progressions 1076 ) 1077 1078 profile_string_vars: typing.Optional[collections.Mapping[int, int]] = None 1079 if raw_profile_string_vars := payload.get("profileStringVariables"): 1080 profile_string_vars = raw_profile_string_vars["data"]["integerValuesByHash"] 1081 1082 character_string_vars: typing.Optional[ 1083 collections.Mapping[int, collections.Mapping[int, int]] 1084 ] = None 1085 if raw_character_string_vars := payload.get("characterStringVariables"): 1086 character_string_vars = { 1087 int(char_id): data["integerValuesByHash"] 1088 for char_id, data in raw_character_string_vars["data"].items() 1089 } 1090 1091 metrics: typing.Optional[ 1092 collections.Sequence[ 1093 collections.Mapping[int, tuple[bool, records.Objective | None]] 1094 ] 1095 ] = None 1096 root_node_hash: int | None = None 1097 1098 if raw_metrics := payload.get("metrics"): 1099 root_node_hash = raw_metrics["data"]["metricsRootNodeHash"] 1100 metrics = tuple( 1101 { 1102 int(metrics_hash): ( 1103 data["invisible"], 1104 self.deserialize_objectives(data["objectiveProgress"]) 1105 if "objectiveProgress" in data 1106 else None, 1107 ) 1108 } 1109 for metrics_hash, data in raw_metrics["data"]["metrics"].items() 1110 ) 1111 transitory: fireteams.FireteamParty | None = None 1112 if raw_transitory := payload.get("profileTransitoryData"): 1113 if "data" in raw_transitory: 1114 transitory = self.deserialize_fireteam_party(raw_transitory["data"]) 1115 1116 item_components: components.ItemsComponent | None = None 1117 if raw_item_components := payload.get("itemComponents"): 1118 item_components = self.deserialize_items_component(raw_item_components) 1119 1120 profile_plugsets: typing.Optional[ 1121 collections.Mapping[int, collections.Sequence[items.PlugItemState]] 1122 ] = None 1123 1124 if raw_profile_plugs := payload.get("profilePlugSets"): 1125 profile_plugsets = { 1126 int(index): [self.deserialize_plug_item_state(state) for state in data] 1127 for index, data in raw_profile_plugs["data"]["plugs"].items() 1128 } 1129 1130 character_plugsets: typing.Optional[ 1131 collections.Mapping[ 1132 int, collections.Mapping[int, collections.Sequence[items.PlugItemState]] 1133 ] 1134 ] = None 1135 if raw_char_plugsets := payload.get("characterPlugSets"): 1136 character_plugsets = { 1137 int(char_id): { 1138 int(index): [ 1139 self.deserialize_plug_item_state(state) for state in data 1140 ] 1141 for index, data in inner["plugs"].items() 1142 } 1143 for char_id, inner in raw_char_plugsets["data"].items() 1144 } 1145 1146 character_collectibles: typing.Optional[ 1147 collections.Mapping[int, items.Collectible] 1148 ] = None 1149 if raw_character_collectibles := payload.get("characterCollectibles"): 1150 character_collectibles = { 1151 int(char_id): self._deserialize_collectible(data) 1152 for char_id, data in raw_character_collectibles["data"].items() 1153 } 1154 1155 profile_collectibles: items.Collectible | None = None 1156 if raw_profile_collectibles := payload.get("profileCollectibles"): 1157 profile_collectibles = self._deserialize_collectible( 1158 raw_profile_collectibles["data"] 1159 ) 1160 1161 profile_nodes: typing.Optional[collections.Mapping[int, records.Node]] = None 1162 if raw_profile_nodes := payload.get("profilePresentationNodes"): 1163 profile_nodes = { 1164 int(node_hash): self._deserialize_node(node) 1165 for node_hash, node in raw_profile_nodes["data"]["nodes"].items() 1166 } 1167 1168 character_nodes: typing.Optional[ 1169 collections.Mapping[int, collections.Mapping[int, records.Node]] 1170 ] = None 1171 if raw_character_nodes := payload.get("characterPresentationNodes"): 1172 character_nodes = { 1173 int(char_id): { 1174 int(node_hash): self._deserialize_node(node) 1175 for node_hash, node in each_character["nodes"].items() 1176 } 1177 for char_id, each_character in raw_character_nodes["data"].items() 1178 } 1179 1180 platform_silver: typing.Optional[ 1181 collections.Mapping[str, profile.ProfileItemImpl] 1182 ] = None 1183 if raw_platform_silver := payload.get("platformSilver"): 1184 if "data" in raw_platform_silver: 1185 platform_silver = { 1186 platform_name: self.deserialize_profile_item(item) 1187 for platform_name, item in raw_platform_silver["data"][ 1188 "platformSilver" 1189 ].items() 1190 } 1191 1192 character_currency_lookups: typing.Optional[ 1193 collections.Mapping[int, collections.Sequence[items.Currency]] 1194 ] = None 1195 if raw_char_lookups := payload.get("characterCurrencyLookups"): 1196 if "data" in raw_char_lookups: 1197 character_currency_lookups = { 1198 int(char_id): self._deserialize_currencies(currency) 1199 for char_id, currency in raw_char_lookups["data"].items() 1200 } 1201 1202 character_craftables: typing.Optional[ 1203 collections.Mapping[int, components.CraftablesComponent] 1204 ] = None 1205 if raw_character_craftables := payload.get("characterCraftables"): 1206 if "data" in raw_character_craftables: 1207 character_craftables = { 1208 int(char_id): self.deserialize_craftables_component(craftable) 1209 for char_id, craftable in raw_character_craftables["data"].items() 1210 } 1211 1212 return components.Component( 1213 profiles=profile_, 1214 profile_progression=profile_progression, 1215 profile_currencies=profile_currencies, 1216 profile_inventories=profile_inventories, 1217 profile_records=profile_records, 1218 characters=characters, 1219 character_records=character_records, 1220 character_equipments=character_equipments, 1221 character_inventories=character_inventories, 1222 character_activities=character_activities, 1223 character_render_data=character_render_data, 1224 character_progressions=character_progressions, 1225 profile_string_variables=profile_string_vars, 1226 character_string_variables=character_string_vars, 1227 metrics=metrics, 1228 root_node_hash=root_node_hash, 1229 transitory=transitory, 1230 item_components=item_components, 1231 profile_plugsets=profile_plugsets, 1232 character_plugsets=character_plugsets, 1233 character_collectibles=character_collectibles, 1234 profile_collectibles=profile_collectibles, 1235 profile_nodes=profile_nodes, 1236 character_nodes=character_nodes, 1237 platform_silver=platform_silver, 1238 character_currency_lookups=character_currency_lookups, 1239 character_craftables=character_craftables, 1240 )
Deserialize a JSON payload of Bungie.net profile components information.
Parameters
- payload (
aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
aiobungie.crates.Component: A component implementation that includes all other components of the deserialized payload.
1242 def deserialize_items_component( 1243 self, payload: typedefs.JSONObject 1244 ) -> components.ItemsComponent: 1245 # Due to how complex this method is, We'll stick to typing.Optional. 1246 instances: typing.Optional[ 1247 collections.Sequence[collections.Mapping[int, items.ItemInstance]] 1248 ] = None 1249 if raw_instances := payload.get("instances"): 1250 instances = tuple( 1251 {int(ins_id): self.deserialize_instanced_item(item)} 1252 for ins_id, item in raw_instances["data"].items() 1253 ) 1254 1255 render_data: typing.Optional[ 1256 collections.Mapping[int, tuple[bool, dict[int, int]]] 1257 ] = None 1258 if raw_render_data := payload.get("renderData"): 1259 render_data = { 1260 int(ins_id): (data["useCustomDyes"], data["artRegions"]) 1261 for ins_id, data in raw_render_data["data"].items() 1262 } 1263 1264 stats: typing.Optional[collections.Mapping[int, items.ItemStatsView]] = None 1265 if raw_stats := payload.get("stats"): 1266 stats = {} 1267 for ins_id, stat in raw_stats["data"].items(): 1268 for _, items_ in stat.items(): 1269 stats[int(ins_id)] = self.deserialize_item_stats_view(items_) 1270 1271 sockets: typing.Optional[ 1272 collections.Mapping[int, collections.Sequence[items.ItemSocket]] 1273 ] = None 1274 if raw_sockets := payload.get("sockets"): 1275 sockets = { 1276 int(ins_id): tuple( 1277 self.deserialize_item_socket(socket) for socket in item["sockets"] 1278 ) 1279 for ins_id, item in raw_sockets["data"].items() 1280 } 1281 1282 objectives: typing.Optional[ 1283 collections.Mapping[int, collections.Sequence[records.Objective]] 1284 ] = None 1285 if raw_objectives := payload.get("objectives"): 1286 objectives = { 1287 int(ins_id): tuple( 1288 self.deserialize_objectives(objective) 1289 for objective in data["objectives"] 1290 ) 1291 for ins_id, data in raw_objectives["data"].items() 1292 } 1293 1294 perks: typing.Optional[ 1295 collections.Mapping[int, collections.Collection[items.ItemPerk]] 1296 ] = None 1297 if raw_perks := payload.get("perks"): 1298 perks = { 1299 int(ins_id): tuple( 1300 self.deserialize_item_perk(perk) for perk in item["perks"] 1301 ) 1302 for ins_id, item in raw_perks["data"].items() 1303 } 1304 1305 plug_states: typing.Optional[collections.Sequence[items.PlugItemState]] = None 1306 if raw_plug_states := payload.get("plugStates"): 1307 plug_states = tuple( 1308 self.deserialize_plug_item_state(plug) 1309 for _, plug in raw_plug_states["data"].items() 1310 ) 1311 1312 reusable_plugs: typing.Optional[ 1313 collections.Mapping[int, collections.Sequence[items.PlugItemState]] 1314 ] = None 1315 if raw_re_plugs := payload.get("reusablePlugs"): 1316 reusable_plugs = { 1317 int(ins_id): tuple( 1318 self.deserialize_plug_item_state(state) for state in inner 1319 ) 1320 for ins_id, plug in raw_re_plugs["data"].items() 1321 for inner in tuple(plug["plugs"].values()) 1322 } 1323 1324 plug_objectives: typing.Optional[ 1325 collections.Mapping[ 1326 int, collections.Mapping[int, collections.Collection[records.Objective]] 1327 ] 1328 ] = None 1329 if raw_plug_objectives := payload.get("plugObjectives"): 1330 plug_objectives = { 1331 int(ins_id): { 1332 int(obj_hash): tuple( 1333 self.deserialize_objectives(obj) for obj in objs 1334 ) 1335 for obj_hash, objs in inner["objectivesPerPlug"].items() 1336 } 1337 for ins_id, inner in raw_plug_objectives["data"].items() 1338 } 1339 1340 return components.ItemsComponent( 1341 sockets=sockets, 1342 stats=stats, 1343 render_data=render_data, 1344 instances=instances, 1345 objectives=objectives, 1346 perks=perks, 1347 plug_states=plug_states, 1348 reusable_plugs=reusable_plugs, 1349 plug_objectives=plug_objectives, 1350 )
Deserialize a JSON objects within the itemComponents key.`
1352 def deserialize_character_component( 1353 self, payload: typedefs.JSONObject 1354 ) -> components.CharacterComponent: 1355 character_: character.Character | None = None 1356 if raw_singular_character := payload.get("character"): 1357 character_ = self.deserialize_character(raw_singular_character["data"]) 1358 1359 inventory: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None 1360 if raw_inventory := payload.get("inventory"): 1361 if "data" in raw_inventory: 1362 inventory = self.deserialize_profile_items(raw_inventory["data"]) 1363 1364 activities: activity.CharacterActivity | None = None 1365 if raw_activities := payload.get("activities"): 1366 activities = self.deserialize_character_activity(raw_activities["data"]) 1367 1368 equipment: typing.Optional[collections.Sequence[profile.ProfileItemImpl]] = None 1369 if raw_equipments := payload.get("equipment"): 1370 equipment = self.deserialize_profile_items(raw_equipments["data"]) 1371 1372 progressions_: character.CharacterProgression | None = None 1373 if raw_progressions := payload.get("progressions"): 1374 progressions_ = self.deserialize_character_progressions( 1375 raw_progressions["data"] 1376 ) 1377 1378 render_data: character.RenderedData | None = None 1379 if raw_render_data := payload.get("renderData"): 1380 render_data = self.deserialize_character_render_data( 1381 raw_render_data["data"] 1382 ) 1383 1384 character_records: typing.Optional[ 1385 collections.Mapping[int, records.CharacterRecord] 1386 ] = None 1387 if raw_char_records := payload.get("records"): 1388 character_records = self.deserialize_characters_records( 1389 raw_char_records["data"] 1390 ) 1391 1392 item_components: components.ItemsComponent | None = None 1393 if raw_item_components := payload.get("itemComponents"): 1394 item_components = self.deserialize_items_component(raw_item_components) 1395 1396 nodes: typing.Optional[collections.Mapping[int, records.Node]] = None 1397 if raw_nodes := payload.get("presentationNodes"): 1398 nodes = { 1399 int(node_hash): self._deserialize_node(node) 1400 for node_hash, node in raw_nodes["data"]["nodes"].items() 1401 } 1402 1403 collectibles: items.Collectible | None = None 1404 if raw_collectibles := payload.get("collectibles"): 1405 collectibles = self._deserialize_collectible(raw_collectibles["data"]) 1406 1407 currency_lookups: typing.Optional[collections.Sequence[items.Currency]] = None 1408 if raw_currencies := payload.get("currencyLookups"): 1409 if "data" in raw_currencies: 1410 currency_lookups = self._deserialize_currencies(raw_currencies) 1411 1412 return components.CharacterComponent( 1413 activities=activities, 1414 equipment=equipment, 1415 inventory=inventory, 1416 progressions=progressions_, 1417 render_data=render_data, 1418 character=character_, 1419 character_records=character_records, 1420 profile_records=None, 1421 item_components=item_components, 1422 currency_lookups=currency_lookups, 1423 collectibles=collectibles, 1424 nodes=nodes, 1425 )
Deserialize a JSON payload of Destiny 2 character component.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.CharacterComponent: A character component.
1444 def deserialize_inventory_results( 1445 self, payload: typedefs.JSONObject 1446 ) -> iterators.Iterator[entity.SearchableEntity]: 1447 return iterators.Iterator( 1448 [ 1449 entity.SearchableEntity( 1450 net=self._net, 1451 hash=data["hash"], 1452 entity_type=data["entityType"], 1453 weight=data["weight"], 1454 suggested_words=payload["suggestedWords"], 1455 name=data["displayProperties"]["name"], 1456 has_icon=data["displayProperties"]["hasIcon"], 1457 description=typedefs.unknown( 1458 data["displayProperties"]["description"] 1459 ), 1460 icon=assets.Image(path=data["displayProperties"]["icon"]), 1461 ) 1462 for data in payload["results"]["results"] 1463 ] 1464 )
Deserialize results of searched Destiny2 entities.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.Iterator[aiobungie.crates.SearchableEntity]: An iterator over the found searched entities.
1493 def deserialize_inventory_entity( # noqa: C901 Too complex. 1494 self, payload: typedefs.JSONObject, / 1495 ) -> entity.InventoryEntity: 1496 props = self._set_entity_attrs(payload) 1497 objects = self._deserialize_inventory_item_objects(payload) 1498 1499 collectible_hash: int | None = None 1500 if raw_collectible_hash := payload.get("collectibleHash"): 1501 collectible_hash = int(raw_collectible_hash) 1502 1503 secondary_icon: assets.Image | None = None 1504 if raw_second_icon := payload.get("secondaryIcon"): 1505 secondary_icon = assets.Image(path=raw_second_icon) 1506 1507 secondary_overlay: assets.Image | None = None 1508 if raw_second_overlay := payload.get("secondaryOverlay"): 1509 secondary_overlay = assets.Image(path=raw_second_overlay) 1510 1511 secondary_special: assets.Image | None = None 1512 if raw_second_special := payload.get("secondarySpecial"): 1513 secondary_special = assets.Image(path=raw_second_special) 1514 1515 screenshot: assets.Image | None = None 1516 if raw_screenshot := payload.get("screenshot"): 1517 screenshot = assets.Image(path=raw_screenshot) 1518 1519 watermark_icon: assets.Image | None = None 1520 if raw_watermark_icon := payload.get("iconWatermark"): 1521 watermark_icon = assets.Image(path=raw_watermark_icon) 1522 1523 watermark_shelved: assets.Image | None = None 1524 if raw_watermark_shelved := payload.get("iconWatermarkShelved"): 1525 watermark_shelved = assets.Image(path=raw_watermark_shelved) 1526 1527 about: str | None = None 1528 if raw_about := payload.get("flavorText"): 1529 about = raw_about 1530 1531 ui_item_style: str | None = None 1532 if raw_ui_style := payload.get("uiItemDisplayStyle"): 1533 ui_item_style = raw_ui_style 1534 1535 tier_and_name: str | None = None 1536 if raw_tier_and_name := payload.get("itemTypeAndTierDisplayName"): 1537 tier_and_name = raw_tier_and_name 1538 1539 type_name: str | None = None 1540 if raw_type_name := payload.get("itemTypeDisplayName"): 1541 type_name = raw_type_name 1542 1543 display_source: str | None = None 1544 if raw_display_source := payload.get("displaySource"): 1545 display_source = raw_display_source 1546 1547 lorehash: int | None = None 1548 if raw_lore_hash := payload.get("loreHash"): 1549 lorehash = int(raw_lore_hash) 1550 1551 summary_hash: int | None = None 1552 if raw_summary_hash := payload.get("summaryItemHash"): 1553 summary_hash = raw_summary_hash 1554 1555 breaker_type_hash: int | None = None 1556 if raw_breaker_type_hash := payload.get("breakerTypeHash"): 1557 breaker_type_hash = int(raw_breaker_type_hash) 1558 1559 damage_types: typing.Optional[collections.Sequence[int]] = None 1560 if raw_damage_types := payload.get("damageTypes"): 1561 damage_types = tuple(int(type_) for type_ in raw_damage_types) 1562 1563 damagetype_hashes: typing.Optional[collections.Sequence[int]] = None 1564 if raw_damagetype_hashes := payload.get("damageTypeHashes"): 1565 damagetype_hashes = tuple(int(type_) for type_ in raw_damagetype_hashes) 1566 1567 default_damagetype_hash: int | None = None 1568 if raw_defaultdmg_hash := payload.get("defaultDamageTypeHash"): 1569 default_damagetype_hash = int(raw_defaultdmg_hash) 1570 1571 emblem_objective_hash: int | None = None 1572 if raw_emblem_obj_hash := payload.get("emblemObjectiveHash"): 1573 emblem_objective_hash = int(raw_emblem_obj_hash) 1574 1575 tier_type: enums.TierType | None = None 1576 tier: enums.ItemTier | None = None 1577 bucket_hash: int | None = None 1578 recovery_hash: int | None = None 1579 tier_name: str | None = None 1580 isinstance_item: bool = False 1581 expire_tool_tip: str | None = None 1582 expire_in_orbit_message: str | None = None 1583 suppress_expiration: bool = False 1584 max_stack_size: int | None = None 1585 stack_label: str | None = None 1586 1587 if inventory := payload.get("inventory"): 1588 tier_type = enums.TierType(int(inventory["tierType"])) 1589 tier = enums.ItemTier(int(inventory["tierTypeHash"])) 1590 bucket_hash = int(inventory["bucketTypeHash"]) 1591 recovery_hash = int(inventory["recoveryBucketTypeHash"]) 1592 tier_name = inventory["tierTypeName"] 1593 isinstance_item = inventory["isInstanceItem"] 1594 suppress_expiration = inventory["suppressExpirationWhenObjectivesComplete"] 1595 max_stack_size = int(inventory["maxStackSize"]) 1596 1597 try: 1598 stack_label = inventory["stackUniqueLabel"] 1599 except KeyError: 1600 pass 1601 1602 if "traitHashes" in payload: 1603 trait_hashes = tuple( 1604 int(trait_hash) for trait_hash in payload["traitHashes"] 1605 ) 1606 else: 1607 trait_hashes = () 1608 1609 if "traitIds" in payload: 1610 trait_ids = tuple(trait_id for trait_id in payload["traitIds"]) 1611 else: 1612 trait_ids = () 1613 1614 return entity.InventoryEntity( 1615 net=self._net, 1616 collectible_hash=collectible_hash, 1617 name=props.name, 1618 about=about, 1619 emblem_objective_hash=emblem_objective_hash, 1620 suppress_expiration=suppress_expiration, 1621 max_stack_size=max_stack_size, 1622 stack_label=stack_label, 1623 tier=tier, 1624 tier_type=tier_type, 1625 tier_name=tier_name, 1626 bucket_hash=bucket_hash, 1627 recovery_bucket_hash=recovery_hash, 1628 isinstance_item=isinstance_item, 1629 expire_in_orbit_message=expire_in_orbit_message, 1630 expiration_tooltip=expire_tool_tip, 1631 lore_hash=lorehash, 1632 type_and_tier_name=tier_and_name, 1633 summary_hash=summary_hash, 1634 ui_display_style=ui_item_style, 1635 type_name=type_name, 1636 breaker_type_hash=breaker_type_hash, 1637 description=props.description, 1638 display_source=display_source, 1639 hash=props.hash, 1640 damage_types=damage_types, 1641 index=props.index, 1642 icon=props.icon, 1643 has_icon=props.has_icon, 1644 screenshot=screenshot, 1645 watermark_icon=watermark_icon, 1646 watermark_shelved=watermark_shelved, 1647 secondary_icon=secondary_icon, 1648 secondary_overlay=secondary_overlay, 1649 secondary_special=secondary_special, 1650 type=enums.ItemType(int(payload["itemType"])), 1651 category_hashes=tuple( 1652 int(hash_) for hash_ in payload["itemCategoryHashes"] 1653 ), 1654 item_class=enums.Class(int(payload["classType"])), 1655 sub_type=enums.ItemSubType(int(payload["itemSubType"])), 1656 breaker_type=int(payload["breakerType"]), 1657 default_damagetype=int(payload["defaultDamageType"]), 1658 default_damagetype_hash=default_damagetype_hash, 1659 damagetype_hashes=damagetype_hashes, 1660 tooltip_notifications=payload["tooltipNotifications"], 1661 not_transferable=payload["nonTransferrable"], 1662 allow_actions=payload["allowActions"], 1663 is_equippable=payload["equippable"], 1664 objects=objects, 1665 background_colors=payload.get("backgroundColor", {}), 1666 season_hash=payload.get("seasonHash"), 1667 has_postmaster_effect=payload["doesPostmasterPullHaveSideEffects"], 1668 trait_hashes=trait_hashes, 1669 trait_ids=trait_ids, 1670 )
Deserialize a JSON payload of an inventory entity item information.
This can be any item from DestinyInventoryItemDefinition definition.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.InventoryEntity: An entity item.
1672 def deserialize_objective_entity( 1673 self, payload: typedefs.JSONObject, / 1674 ) -> entity.ObjectiveEntity: 1675 props = self._set_entity_attrs(payload) 1676 return entity.ObjectiveEntity( 1677 net=self._net, 1678 hash=props.hash, 1679 index=props.index, 1680 description=props.description, 1681 name=props.name, 1682 has_icon=props.has_icon, 1683 icon=props.icon, 1684 unlock_value_hash=payload["unlockValueHash"], 1685 completion_value=payload["completionValue"], 1686 scope=entity.GatingScope(int(payload["scope"])), 1687 location_hash=payload["locationHash"], 1688 allowed_negative_value=payload["allowNegativeValue"], 1689 allowed_value_change=payload["allowValueChangeWhenCompleted"], 1690 counting_downward=payload["isCountingDownward"], 1691 value_style=entity.ValueUIStyle(int(payload["valueStyle"])), 1692 progress_description=payload["progressDescription"], 1693 perks=payload["perks"], 1694 stats=payload["stats"], 1695 minimum_visibility=payload["minimumVisibilityThreshold"], 1696 allow_over_completion=payload["allowOvercompletion"], 1697 show_value_style=payload["showValueOnComplete"], 1698 display_only_objective=payload["isDisplayOnlyObjective"], 1699 complete_value_style=entity.ValueUIStyle( 1700 int(payload["completedValueStyle"]) 1701 ), 1702 progress_value_style=entity.ValueUIStyle( 1703 int(payload["inProgressValueStyle"]) 1704 ), 1705 ui_label=payload["uiLabel"], 1706 ui_style=entity.ObjectiveUIStyle(int(payload["uiStyle"])), 1707 )
Deserialize a JSON payload of an objective entity information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.ObjectiveEntity: An objective entity.
1735 def deserialize_activity( 1736 self, 1737 payload: typedefs.JSONObject, 1738 /, 1739 ) -> activity.Activity: 1740 period = time.clean_date(payload["period"]) 1741 details = payload["activityDetails"] 1742 ref_id = int(details["referenceId"]) 1743 instance_id = int(details["instanceId"]) 1744 mode = enums.GameMode(details["mode"]) 1745 modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"]) 1746 is_private = details["isPrivate"] 1747 membership_type = enums.MembershipType(int(details["membershipType"])) 1748 1749 # Since we're using the same fields for post activity method 1750 # this check is required since post activity doesn't values values 1751 values = self._deserialize_activity_values(payload["values"]) 1752 1753 return activity.Activity( 1754 net=self._net, 1755 hash=ref_id, 1756 instance_id=instance_id, 1757 mode=mode, 1758 modes=modes, 1759 is_private=is_private, 1760 membership_type=membership_type, 1761 occurred_at=period, 1762 values=values, 1763 )
Deserialize a JSON payload of an activity history information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.Activity: An activity.
1765 def deserialize_activities( 1766 self, payload: typedefs.JSONObject 1767 ) -> iterators.Iterator[activity.Activity]: 1768 return iterators.Iterator( 1769 [ 1770 self.deserialize_activity(activity_) 1771 for activity_ in payload["activities"] 1772 ] 1773 )
Deserialize a JSON payload of an array of activity history information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.Iterator[aiobungie.crates.Activity]: Am iterator over activity objects of the deserialized payload.
1775 def deserialize_extended_weapon_values( 1776 self, payload: typedefs.JSONObject 1777 ) -> activity.ExtendedWeaponValues: 1778 assists: int | None = None 1779 if raw_assists := payload["values"].get("uniqueWeaponAssists"): 1780 assists = raw_assists["basic"]["value"] 1781 assists_damage: int | None = None 1782 1783 if raw_assists_damage := payload["values"].get("uniqueWeaponAssistDamage"): 1784 assists_damage = raw_assists_damage["basic"]["value"] 1785 1786 return activity.ExtendedWeaponValues( 1787 reference_id=int(payload["referenceId"]), 1788 kills=payload["values"]["uniqueWeaponKills"]["basic"]["value"], 1789 precision_kills=payload["values"]["uniqueWeaponPrecisionKills"]["basic"][ 1790 "value" 1791 ], 1792 assists=assists, 1793 assists_damage=assists_damage, 1794 precision_kills_percentage=( 1795 payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"]["value"], 1796 payload["values"]["uniqueWeaponKillsPrecisionKills"]["basic"][ 1797 "displayValue" 1798 ], 1799 ), 1800 )
Deserialize values of extended weapons JSON object.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.ExtendedWeaponValues: Information about an extended weapon values.
1821 def deserialize_post_activity_player( 1822 self, payload: typedefs.JSONObject, / 1823 ) -> activity.PostActivityPlayer: 1824 player = payload["player"] 1825 1826 class_hash: int | None = None 1827 if (class_hash := player.get("classHash")) is not None: 1828 class_hash = class_hash 1829 1830 race_hash: int | None = None 1831 if (race_hash := player.get("raceHash")) is not None: 1832 race_hash = race_hash 1833 1834 gender_hash: int | None = None 1835 if (gender_hash := player.get("genderHash")) is not None: 1836 gender_hash = gender_hash 1837 1838 character_class: str | None = None 1839 if character_class := player.get("characterClass"): 1840 character_class = character_class 1841 1842 character_level: int | None = None 1843 if (character_level := player.get("characterLevel")) is not None: 1844 character_level = character_level 1845 1846 return activity.PostActivityPlayer( 1847 standing=int(payload["standing"]), 1848 score=int(payload["score"]["basic"]["value"]), 1849 character_id=payload["characterId"], 1850 destiny_user=self.deserialize_destiny_membership(player["destinyUserInfo"]), 1851 character_class=character_class, 1852 character_level=character_level, 1853 race_hash=race_hash, 1854 gender_hash=gender_hash, 1855 class_hash=class_hash, 1856 light_level=int(player["lightLevel"]), 1857 emblem_hash=int(player["emblemHash"]), 1858 values=self._deserialize_activity_values(payload["values"]), 1859 extended_values=self._deserialize_extended_values(payload["extended"]), 1860 )
Deserialize a JSON payload of a post activity player information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.PostActivityPlayer: A post activity player object.
1872 def deserialize_post_activity( 1873 self, payload: typedefs.JSONObject 1874 ) -> activity.PostActivity: 1875 period = time.clean_date(payload["period"]) 1876 details = payload["activityDetails"] 1877 ref_id = int(details["referenceId"]) 1878 instance_id = int(details["instanceId"]) 1879 mode = enums.GameMode(details["mode"]) 1880 modes = tuple(enums.GameMode(int(mode_)) for mode_ in details["modes"]) 1881 is_private = details["isPrivate"] 1882 membership_type = enums.MembershipType(int(details["membershipType"])) 1883 return activity.PostActivity( 1884 net=self._net, 1885 hash=ref_id, 1886 membership_type=membership_type, 1887 instance_id=instance_id, 1888 mode=mode, 1889 modes=modes, 1890 is_private=is_private, 1891 occurred_at=period, 1892 starting_phase=int(payload["startingPhaseIndex"]), 1893 players=tuple( 1894 self.deserialize_post_activity_player(player) 1895 for player in payload["entries"] 1896 ), 1897 teams=tuple( 1898 self._deserialize_post_activity_team(team) for team in payload["teams"] 1899 ), 1900 )
Deserialize a JSON payload of a post activity information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.PostActivity: A post activity object.
1938 def deserialize_aggregated_activity( 1939 self, payload: typedefs.JSONObject 1940 ) -> activity.AggregatedActivity: 1941 return activity.AggregatedActivity( 1942 hash=int(payload["activityHash"]), 1943 values=self._deserialize_aggregated_activity_values(payload["values"]), 1944 )
Deserialize a JSON payload of an aggregated activity.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.AggregatedActivity: An aggregated activity object.
1946 def deserialize_aggregated_activities( 1947 self, payload: typedefs.JSONObject 1948 ) -> iterators.Iterator[activity.AggregatedActivity]: 1949 return iterators.Iterator( 1950 [ 1951 self.deserialize_aggregated_activity(activity) 1952 for activity in payload["activities"] 1953 ] 1954 )
Deserialize a JSON payload of an array of aggregated activities.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.Iterator[aiobungie.crates.AggregatedActivity]: An iterator over aggregated activities objects.
1956 def deserialize_linked_profiles( 1957 self, payload: typedefs.JSONObject 1958 ) -> profile.LinkedProfile: 1959 bungie_user = self.deserialize_partial_bungie_user(payload["bnetMembership"]) 1960 1961 if raw_profile := payload.get("profiles"): 1962 profiles = tuple( 1963 self.deserialize_destiny_membership(p) for p in raw_profile 1964 ) 1965 else: 1966 profiles = () 1967 1968 error_profiles = () 1969 if raw_profiles_with_errors := payload.get("profilesWithErrors"): 1970 for raw_error_p in raw_profiles_with_errors: 1971 if "infoCard" in raw_error_p: 1972 error_profiles = tuple( 1973 self.deserialize_destiny_membership(error_p) 1974 for error_p in raw_error_p 1975 ) 1976 1977 return profile.LinkedProfile( 1978 bungie_user=bungie_user, 1979 profiles=profiles, 1980 profiles_with_errors=error_profiles, 1981 )
Deserialize a JSON payload of Bungie.net hard linked profile information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.LinkedProfile: A hard linked profile.
1999 def deserialize_public_milestone_content( 2000 self, payload: typedefs.JSONObject 2001 ) -> milestones.MilestoneContent: 2002 if raw_categories := payload.get("itemCategories"): 2003 items_categories = tuple( 2004 milestones.MilestoneItems( 2005 title=item["title"], hashes=item["itemHashes"] 2006 ) 2007 for item in raw_categories 2008 ) 2009 else: 2010 items_categories = () 2011 2012 return milestones.MilestoneContent( 2013 about=typedefs.unknown(payload["about"]), 2014 status=typedefs.unknown(payload["status"]), 2015 tips=payload.get("tips", ()), 2016 items=items_categories, 2017 )
Deserialize a JSON payload of milestone content information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.MilestoneContent: A milestone content.
2019 def deserialize_friend(self, payload: typedefs.JSONObject, /) -> friends.Friend: 2020 bungie_user: user.BungieUser | None = None 2021 2022 if raw_bungie_user := payload.get("bungieNetUser"): 2023 bungie_user = self.deserialize_bungie_user(raw_bungie_user) 2024 2025 return friends.Friend( 2026 net=self._net, 2027 id=int(payload["lastSeenAsMembershipId"]), 2028 name=typedefs.unknown(payload["bungieGlobalDisplayName"]), 2029 code=payload.get("bungieGlobalDisplayNameCode"), 2030 relationship=enums.Relationship(payload["relationship"]), 2031 user=bungie_user, 2032 online_status=enums.Presence(payload["onlineStatus"]), 2033 online_title=payload["onlineTitle"], 2034 type=enums.MembershipType(payload["lastSeenAsBungieMembershipType"]), 2035 )
Deserialize a JSON payload of a Bungie friend information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.Friend: A friend.
2037 def deserialize_friends( 2038 self, payload: typedefs.JSONObject 2039 ) -> collections.Sequence[friends.Friend]: 2040 return tuple(self.deserialize_friend(friend) for friend in payload["friends"])
Deserialize a JSON sequence of Bungie friends information.
This is usually used to deserialize the incoming/outgoing friend requests.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.Friend]: A sequence of friends.
2042 def deserialize_friend_requests( 2043 self, payload: typedefs.JSONObject 2044 ) -> friends.FriendRequestView: 2045 if raw_incoming_requests := payload.get("incomingRequests"): 2046 incoming = tuple( 2047 self.deserialize_friend(incoming_request) 2048 for incoming_request in raw_incoming_requests 2049 ) 2050 else: 2051 incoming = () 2052 2053 if raw_outgoing_requests := payload.get("outgoingRequests"): 2054 outgoing = tuple( 2055 self.deserialize_friend(outgoing_request) 2056 for outgoing_request in raw_outgoing_requests 2057 ) 2058 else: 2059 outgoing = () 2060 2061 return friends.FriendRequestView(incoming=incoming, outgoing=outgoing)
Deserialize a JSON sequence of Bungie friend requests information.
This is used for incoming/outgoing friend requests.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.FriendRequestView]: A sequence of incoming and outgoing friends.
2092 def deserialize_fireteams( 2093 self, payload: typedefs.JSONObject 2094 ) -> collections.Sequence[fireteams.Fireteam]: 2095 if "results" in payload: 2096 fireteams_ = tuple( 2097 self._set_fireteam_fields( 2098 elem, total_results=int(payload["totalResults"]) 2099 ) 2100 for elem in payload["results"] 2101 ) 2102 else: 2103 fireteams_ = () 2104 return fireteams_
Deserialize a JSON sequence of Bungie fireteams information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.Fireteam]: A sequence of fireteam.
2106 def deserialize_fireteam_destiny_users( 2107 self, payload: typedefs.JSONObject 2108 ) -> fireteams.FireteamUser: 2109 destiny_obj = self.deserialize_destiny_membership(payload) 2110 return fireteams.FireteamUser( 2111 net=self._net, 2112 id=destiny_obj.id, 2113 code=destiny_obj.code, 2114 icon=destiny_obj.icon, 2115 types=destiny_obj.types, 2116 type=destiny_obj.type, 2117 is_public=destiny_obj.is_public, 2118 crossave_override=destiny_obj.crossave_override, 2119 name=destiny_obj.name, 2120 last_seen_name=destiny_obj.last_seen_name, 2121 fireteam_display_name=payload["FireteamDisplayName"], 2122 fireteam_membership_id=enums.MembershipType( 2123 payload["FireteamMembershipType"] 2124 ), 2125 )
Deserialize a JSON payload of Bungie fireteam destiny users information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.FireteamUser: A fireteam user.
2127 def deserialize_fireteam_members( 2128 self, payload: typedefs.JSONObject, *, alternatives: bool = False 2129 ) -> collections.Sequence[fireteams.FireteamMember]: 2130 members_: list[fireteams.FireteamMember] = [] 2131 if members := payload.get("Members" if not alternatives else "Alternates"): 2132 for member in members: 2133 bungie_fields = self.deserialize_partial_bungie_user(member) 2134 members_fields = fireteams.FireteamMember( 2135 destiny_user=self.deserialize_fireteam_destiny_users(member), 2136 has_microphone=member["hasMicrophone"], 2137 character_id=int(member["characterId"]), 2138 date_joined=time.clean_date(member["dateJoined"]), 2139 last_platform_invite_date=time.clean_date( 2140 member["lastPlatformInviteAttemptDate"] 2141 ), 2142 last_platform_invite_result=int( 2143 member["lastPlatformInviteAttemptResult"] 2144 ), 2145 net=self._net, 2146 name=bungie_fields.name, 2147 id=bungie_fields.id, 2148 icon=bungie_fields.icon, 2149 is_public=bungie_fields.is_public, 2150 crossave_override=bungie_fields.crossave_override, 2151 types=bungie_fields.types, 2152 type=bungie_fields.type, 2153 ) 2154 members_.append(members_fields) 2155 return tuple(members_)
Deserialize a JSON sequence of Bungie fireteam members information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload. - alternatives (
bool): If set toTrue, Then it will deserialize thealternativesdata in the payload. If not the it will just deserialize themembersdata.
Returns
collections.Sequence[aiobungie.crates.FireteamUser]: A sequence of the fireteam members.
2157 def deserialize_available_fireteam( 2158 self, payload: typedefs.JSONObject 2159 ) -> fireteams.AvailableFireteam: 2160 fields = self._set_fireteam_fields(payload["Summary"]) 2161 return fireteams.AvailableFireteam( 2162 id=fields.id, 2163 group_id=fields.group_id, 2164 platform=fields.platform, 2165 activity_type=fields.activity_type, 2166 is_immediate=fields.is_immediate, 2167 is_public=fields.is_public, 2168 is_valid=fields.is_valid, 2169 owner_id=fields.owner_id, 2170 player_slot_count=fields.player_slot_count, 2171 available_player_slots=fields.available_player_slots, 2172 available_alternate_slots=fields.available_alternate_slots, 2173 title=fields.title, 2174 date_created=fields.date_created, 2175 locale=fields.locale, 2176 last_modified=fields.last_modified, 2177 total_results=fields.total_results, 2178 scheduled_time=fields.scheduled_time, 2179 date_modified=fields.date_modified, 2180 members=self.deserialize_fireteam_members(payload), 2181 alternatives=self.deserialize_fireteam_members(payload, alternatives=True), 2182 )
Deserialize a JSON payload of a sequence of/fireteam information.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
- An available fireteam object.
2184 def deserialize_available_fireteams( 2185 self, data: typedefs.JSONObject 2186 ) -> collections.Sequence[fireteams.AvailableFireteam]: 2187 if raw_results := data.get("results"): 2188 fireteam_results = tuple( 2189 self.deserialize_available_fireteam(f) for f in raw_results 2190 ) 2191 else: 2192 fireteam_results = () 2193 return fireteam_results
Deserialize a JSON payload sequence of fireteam objects.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
collections.Sequence[aiobungie.crates.fireteams.AvailableFireteam]: A sequence of available fireteams.
2195 def deserialize_fireteam_party( 2196 self, payload: typedefs.JSONObject 2197 ) -> fireteams.FireteamParty: 2198 last_destination_hash: int | None = None 2199 if raw_dest_hash := payload.get("lastOrbitedDestinationHash"): 2200 last_destination_hash = int(raw_dest_hash) 2201 2202 return fireteams.FireteamParty( 2203 members=tuple( 2204 self._deserialize_fireteam_party_member(member) 2205 for member in payload["partyMembers"] 2206 ), 2207 activity=self._deserialize_fireteam_party_current_activity( 2208 payload["currentActivity"] 2209 ), 2210 settings=self._deserialize_fireteam_party_settings(payload["joinability"]), 2211 last_destination_hash=last_destination_hash, 2212 tracking=payload["tracking"], 2213 )
Deserialize a JSON payload of profileTransitory component response.
Parameters
- payload (
aiobungie.typedefs.JSONObject): The JSON payload.
Returns
aiobungie.crates.FireteamParty: A fireteam party object of the current fireteam.
2256 def deserialize_seasonal_artifact( 2257 self, payload: typedefs.JSONObject 2258 ) -> season.Artifact: 2259 raw_artifact = payload["seasonalArtifact"] 2260 2261 points = raw_artifact["pointProgression"] 2262 points_prog = progressions.Progression( 2263 hash=points["progressionHash"], 2264 level=points["level"], 2265 cap=points["levelCap"], 2266 daily_limit=points["dailyLimit"], 2267 weekly_limit=points["weeklyLimit"], 2268 current_progress=points["currentProgress"], 2269 daily_progress=points["dailyProgress"], 2270 needed=points["progressToNextLevel"], 2271 next_level=points["nextLevelAt"], 2272 ) 2273 2274 bonus = raw_artifact["powerBonusProgression"] 2275 power_bonus_prog = progressions.Progression( 2276 hash=bonus["progressionHash"], 2277 level=bonus["level"], 2278 cap=bonus["levelCap"], 2279 daily_limit=bonus["dailyLimit"], 2280 weekly_limit=bonus["weeklyLimit"], 2281 current_progress=bonus["currentProgress"], 2282 daily_progress=bonus["dailyProgress"], 2283 needed=bonus["progressToNextLevel"], 2284 next_level=bonus["nextLevelAt"], 2285 ) 2286 return season.Artifact( 2287 hash=raw_artifact["artifactHash"], 2288 power_bonus=raw_artifact["powerBonus"], 2289 acquired_points=raw_artifact["pointsAcquired"], 2290 bonus=power_bonus_prog, 2291 points=points_prog, 2292 )
Deserialize a JSON payload of a Destiny 2 seasonal artifact information.
Parameters
- payload (
aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
aiobungie.crates.Artifact: A seasonal artifact.
2294 def deserialize_profile_progression( 2295 self, payload: typedefs.JSONObject 2296 ) -> profile.ProfileProgression: 2297 return profile.ProfileProgression( 2298 artifact=self.deserialize_seasonal_artifact(payload["data"]), 2299 checklist={ 2300 int(check_id): checklists 2301 for check_id, checklists in payload["data"]["checklists"].items() 2302 }, 2303 )
Deserialize a JSON payload of a profile progression component.
Parameters
- payload (
aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
aiobungie.crates.ProfileProgression: A profile progression component.
2305 def deserialize_instanced_item( 2306 self, payload: typedefs.JSONObject 2307 ) -> items.ItemInstance: 2308 damage_type_hash: int | None = None 2309 if raw_damagetype_hash := payload.get("damageTypeHash"): 2310 damage_type_hash = int(raw_damagetype_hash) 2311 2312 required_hashes: typing.Optional[collections.Collection[int]] = None 2313 if raw_required_hashes := payload.get("unlockHashesRequiredToEquip"): 2314 required_hashes = tuple(int(raw_hash) for raw_hash in raw_required_hashes) 2315 2316 breaker_type: items.ItemBreakerType | None = None 2317 if raw_break_type := payload.get("breakerType"): 2318 breaker_type = items.ItemBreakerType(int(raw_break_type)) 2319 2320 breaker_type_hash: int | None = None 2321 if raw_break_type_hash := payload.get("breakerTypeHash"): 2322 breaker_type_hash = int(raw_break_type_hash) 2323 2324 energy: items.ItemEnergy | None = None 2325 if raw_energy := payload.get("energy"): 2326 energy = self.deserialize_item_energy(raw_energy) 2327 2328 primary_stats = None 2329 if raw_primary_stats := payload.get("primaryStat"): 2330 primary_stats = self.deserialize_item_stats_view(raw_primary_stats) 2331 2332 return items.ItemInstance( 2333 damage_type=enums.DamageType(int(payload["damageType"])), 2334 damage_type_hash=damage_type_hash, 2335 primary_stat=primary_stats, 2336 item_level=int(payload["itemLevel"]), 2337 quality=int(payload["quality"]), 2338 is_equipped=payload["isEquipped"], 2339 can_equip=payload["canEquip"], 2340 equip_required_level=int(payload["equipRequiredLevel"]), 2341 required_equip_unlock_hashes=required_hashes, 2342 cant_equip_reason=int(payload["cannotEquipReason"]), 2343 breaker_type=breaker_type, 2344 breaker_type_hash=breaker_type_hash, 2345 energy=energy, 2346 )
Deserialize a JSON object into an instanced item.
Parameters
- payload (
aiobungie.internal.helpers.JsonObject): The JSON payload.
Returns
aiobungie.crates.ItemInstance: An instanced item object.
2348 def deserialize_item_energy(self, payload: typedefs.JSONObject) -> items.ItemEnergy: 2349 energy_hash: int | None = None 2350 if raw_energy_hash := payload.get("energyTypeHash"): 2351 energy_hash = int(raw_energy_hash) 2352 2353 return items.ItemEnergy( 2354 hash=energy_hash, 2355 type=items.ItemEnergyType(int(payload["energyType"])), 2356 capacity=int(payload["energyCapacity"]), 2357 used_energy=int(payload["energyUsed"]), 2358 unused_energy=int(payload["energyUnused"]), 2359 )
2361 def deserialize_item_perk(self, payload: typedefs.JSONObject) -> items.ItemPerk: 2362 perk_hash: int | None = None 2363 if raw_perk_hash := payload.get("perkHash"): 2364 perk_hash = int(raw_perk_hash) 2365 2366 return items.ItemPerk( 2367 hash=perk_hash, 2368 icon=assets.Image(path=payload["iconPath"]), 2369 is_active=payload["isActive"], 2370 is_visible=payload["visible"], 2371 )
2373 def deserialize_item_socket(self, payload: typedefs.JSONObject) -> items.ItemSocket: 2374 plug_hash: int | None = None 2375 if raw_plug_hash := payload.get("plugHash"): 2376 plug_hash = int(raw_plug_hash) 2377 2378 enable_fail_indexes: collections.Sequence[int] | None = None 2379 if raw_indexes := payload.get("enableFailIndexes"): 2380 enable_fail_indexes = tuple(int(index) for index in raw_indexes) 2381 2382 return items.ItemSocket( 2383 plug_hash=plug_hash, 2384 is_enabled=payload["isEnabled"], 2385 enable_fail_indexes=enable_fail_indexes, 2386 is_visible=payload.get("visible"), 2387 )
2396 def deserialize_plug_item_state( 2397 self, payload: typedefs.JSONObject 2398 ) -> items.PlugItemState: 2399 item_hash: int | None = None 2400 if raw_item_hash := payload.get("plugItemHash"): 2401 item_hash = int(raw_item_hash) 2402 2403 insert_fail_indexes: collections.Sequence[int] | None = None 2404 if raw_fail_indexes := payload.get("insertFailIndexes"): 2405 insert_fail_indexes = tuple(int(k) for k in raw_fail_indexes) 2406 2407 enable_fail_indexes: collections.Sequence[int] | None = None 2408 if raw_enabled_indexes := payload.get("enableFailIndexes"): 2409 enable_fail_indexes = tuple(int(k) for k in raw_enabled_indexes) 2410 2411 return items.PlugItemState( 2412 item_hash=item_hash, 2413 insert_fail_indexes=insert_fail_indexes, 2414 enable_fail_indexes=enable_fail_indexes, 2415 is_enabled=payload["enabled"], 2416 can_insert=payload["canInsert"], 2417 )
65@typing.final 66class FireteamActivity(int, enums.Enum): 67 """An enum for the fireteam activities.""" 68 69 ALL = 0 70 CRUCIBLE = 2 71 TRIALS_OF_OSIRIS = 3 72 NIGHTFALL = 4 73 ANY = 5 74 GAMBIT = 6 75 BLIND_WELL = 7 76 NIGHTMARE_HUNTS = 12 77 ALTARS_OF_SORROWS = 14 78 DUNGEON = 15 79 RAID_LW = 20 80 RAID_GOS = 21 81 RAID_DSC = 22 82 EXO_CHALLENGE = 23 83 S12_WRATHBORN = 24 84 EMPIRE_HUNTS = 25 85 S13_BATTLEGROUNDS = 26 86 EXOTIC_QUEST = 27 87 RAID_VOG = 28 88 S14_EXPUNGE = 30 89 S15_ASTRAL_ALIGNMENT = 31 90 S15_SHATTERED_RELAM = 32 91 SHATTERED_THRONE = 33 92 PROPHECY = 34 93 PIT_OF_HERESY = 35 94 DOE = 36 95 """Dares of Eternity.""" 96 DUNGEON_GOA = 37 97 """Grasp of Avarice.""" 98 VOW_OF_THE_DISCPILE = 38 99 CAMPAIGN = 39 100 WELLSPRING = 40 101 S16_BATTLEGROUNDS = 41 102 S17_NIGHTMARE_CONTAINMENT = 44 103 S17_SEVER = 45
An enum for the fireteam activities.
129@typing.final 130class FireteamDate(int, enums.Enum): 131 """An enum for fireteam date ranges.""" 132 133 ALL = 0 134 NOW = 1 135 TODAY = 2 136 TWO_DAYS = 3 137 THIS_WEEK = 4
An enum for fireteam date ranges.
106@typing.final 107class FireteamLanguage(str, enums.Enum): 108 """An enum for fireteams languages filters.""" 109 110 ALL = "" 111 ENGLISH = "en" 112 FRENCH = "fr" 113 ESPANOL = "es" 114 DEUTSCH = "de" 115 ITALIAN = "it" 116 JAPANESE = "ja" 117 PORTUGUESE = "pt-br" 118 RUSSIAN = "ru" 119 POLISH = "pl" 120 KOREAN = "ko" 121 # ? China 122 ZH_CHT = "zh-cht" 123 ZH_CHS = "zh-chs" 124 125 def __str__(self) -> str: 126 return str(self.value)
An enum for fireteams languages filters.
Inherited Members
- builtins.str
- encode
- replace
- split
- rsplit
- join
- capitalize
- casefold
- title
- center
- count
- expandtabs
- find
- partition
- index
- ljust
- lower
- lstrip
- rfind
- rindex
- rjust
- rstrip
- rpartition
- splitlines
- strip
- swapcase
- translate
- upper
- startswith
- endswith
- removeprefix
- removesuffix
- isascii
- islower
- isupper
- istitle
- isspace
- isdecimal
- isdigit
- isnumeric
- isalpha
- isalnum
- isidentifier
- isprintable
- zfill
- format
- format_map
- maketrans
52@typing.final 53class FireteamPlatform(int, enums.Enum): 54 """An enum for fireteam related to bungie fireteams. 55 This is different from the normal `aiobungie.MembershipType`. 56 """ 57 58 ANY = 0 59 PSN_NETWORK = 1 60 XBOX_LIVE = 2 61 STEAM = 4 62 STADIA = 5
An enum for fireteam related to bungie fireteams.
This is different from the normal aiobungie.MembershipType.
90class Flag(__enum.Flag): 91 """Builtin Python enum flag with extra handlings.""" 92 93 # Needs to type this here for mypy 94 _value_: int 95 96 @property 97 def name(self) -> str: 98 if self._name_ is None: 99 self._name_ = f"UNKNOWN {self._value_}" 100 101 return self._name_ 102 103 @property 104 def value(self) -> int: 105 return self._value_ 106 107 def __str__(self) -> str: 108 return self.name 109 110 def __repr__(self) -> str: 111 return f"<{type(self).__name__}.{self.name}: {self._value_!s}>" 112 113 def __int__(self) -> int: 114 return int(self.value) 115 116 def __or__(self, other: Flag | int) -> Flag: 117 return self.__class__(self._value_ | int(other)) 118 119 def __xor__(self, other: Flag | int) -> Flag: 120 return self.__class__(self._value_ ^ int(other)) 121 122 def __and__(self, other: Flag | int) -> Flag: 123 return self.__class__(other & int(other)) 124 125 def __invert__(self) -> Flag: 126 return self.__class__(~self._value_) 127 128 def __contains__(self, other: Flag | int) -> bool: 129 return self.value & int(other) == int(other)
Builtin Python enum flag with extra handlings.
136@attrs.define(auto_exc=True) 137class Forbidden(HTTPException): 138 """Exception that's raised for when status code 403 occurs.""" 139 140 http_status: http.HTTPStatus = attrs.field( 141 default=http.HTTPStatus.FORBIDDEN, init=False 142 )
Exception that's raised for when status code 403 occurs.
2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data): 3 self.error_code = error_code 4 self.throttle_seconds = throttle_seconds 5 self.url = url 6 self.body = body 7 self.headers = headers 8 self.message = message 9 self.error_status = error_status 10 self.message_data = message_data 11 self.http_status = attr_dict['http_status'].default 12 BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)
Method generated by attrs for class Forbidden.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
258@typing.final 259class GameMode(int, Enum): 260 """An Enum for all available gamemodes in Destiny 2.""" 261 262 NONE = 0 263 STORY = 2 264 STRIKE = 3 265 RAID = 4 266 ALLPVP = 5 267 PATROL = 6 268 ALLPVE = 7 269 RESERVED9 = 9 270 CONTROL = 10 271 RESERVED11 = 11 272 CLASH = 12 273 RESERVED13 = 13 274 CRIMSONDOUBLES = 15 275 NIGHTFALL = 16 276 HEROICNIGHTFALL = 17 277 ALLSTRIKES = 18 278 IRONBANNER = 19 279 RESERVED20 = 20 280 RESERVED21 = 21 281 RESERVED22 = 22 282 RESERVED24 = 24 283 ALLMAYHEM = 25 284 RESERVED26 = 26 285 RESERVED27 = 27 286 RESERVED28 = 28 287 RESERVED29 = 29 288 RESERVED30 = 30 289 SUPREMACY = 31 290 PRIVATEMATCHESALL = 32 291 SURVIVAL = 37 292 COUNTDOWN = 38 293 TRIALSOFTHENINE = 39 294 SOCIAL = 40 295 TRIALSCOUNTDOWN = 41 296 TRIALSSURVIVAL = 42 297 IRONBANNERCONTROL = 43 298 IRONBANNERCLASH = 44 299 IRONBANNERSUPREMACY = 45 300 SCOREDNIGHTFALL = 46 301 SCOREDHEROICNIGHTFALL = 47 302 RUMBLE = 48 303 ALLDOUBLES = 49 304 DOUBLES = 50 305 PRIVATEMATCHESCLASH = 51 306 PRIVATEMATCHESCONTROL = 52 307 PRIVATEMATCHESSUPREMACY = 53 308 PRIVATEMATCHESCOUNTDOWN = 54 309 PRIVATEMATCHESSURVIVAL = 55 310 PRIVATEMATCHESMAYHEM = 56 311 PRIVATEMATCHESRUMBLE = 57 312 HEROICADVENTURE = 58 313 SHOWDOWN = 59 314 LOCKDOWN = 60 315 SCORCHED = 61 316 SCORCHEDTEAM = 62 317 GAMBIT = 63 318 ALLPVECOMPETITIVE = 64 319 BREAKTHROUGH = 65 320 BLACKARMORYRUN = 66 321 SALVAGE = 67 322 IRONBANNERSALVAGE = 68 323 PVPCOMPETITIVE = 69 324 PVPQUICKPLAY = 70 325 CLASHQUICKPLAY = 71 326 CLASHCOMPETITIVE = 72 327 CONTROLQUICKPLAY = 73 328 CONTROLCOMPETITIVE = 74 329 GAMBITPRIME = 75 330 RECKONING = 76 331 MENAGERIE = 77 332 VEXOFFENSIVE = 78 333 NIGHTMAREHUNT = 79 334 ELIMINATION = 80 335 MOMENTUM = 81 336 DUNGEON = 82 337 SUNDIAL = 83 338 TRIALS_OF_OSIRIS = 84 339 DARES = 85 340 OFFENSIVE = 86 341 LOSTSECTOR = 87 342 RIFT = 88 343 ZONECONTROL = 89 344 IRONBANNERRIFT = 90
An Enum for all available gamemodes in Destiny 2.
57@typing.final 58class GatingScope(int, enums.Enum): 59 """An enum represents restrictive type of gating that is being performed by an entity. 60 61 This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity 62 applies to everyone equally, or to their specific Profile or Character states. 63 """ 64 65 NONE = 0 66 GLOBAL = 1 67 CLAN = 2 68 PROFILE = 3 69 CHARACTER = 4 70 ITEM = 5 71 ASSUMED_WORST_CASE = 6
An enum represents restrictive type of gating that is being performed by an entity.
This is useful as a shortcut to avoid a lot of lookups when determining whether the gating on an Entity applies to everyone equally, or to their specific Profile or Character states.
476@typing.final 477class Gender(int, Enum): 478 """An Enum for Destiny Genders.""" 479 480 MALE = 0 481 FEMALE = 1 482 UNKNOWN = 2
An Enum for Destiny Genders.
645@typing.final 646class GroupType(int, Enum): 647 """An enums for the known bungie group types.""" 648 649 GENERAL = 0 650 CLAN = 1
An enums for the known bungie group types.
75@attrs.define(auto_exc=True) 76class HTTPError(AiobungieError): 77 """Base HTTP request errors exception.""" 78 79 message: str 80 """The error message.""" 81 82 http_status: http.HTTPStatus 83 """The response status."""
Base HTTP request errors exception.
2def __init__(self, message, http_status): 3 self.message = message 4 self.http_status = http_status 5 BaseException.__init__(self, self.message,self.http_status)
Method generated by attrs for class HTTPError.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
86@attrs.define(auto_exc=True, kw_only=True) 87class HTTPException(HTTPError): 88 """An in-depth HTTP exception that's raised with more information.""" 89 90 error_code: int 91 """The returned Bungie error status code.""" 92 93 http_status: http.HTTPStatus 94 """The request response http status.""" 95 96 throttle_seconds: int 97 """The Bungie response throttle seconds.""" 98 99 url: typedefs.StrOrURL | None 100 """The URL/endpoint caused this error.""" 101 102 body: typing.Any 103 """The response body.""" 104 105 headers: multidict.CIMultiDictProxy[str] 106 """The response headers.""" 107 108 message: str 109 """A Bungie human readable message describes the cause of the error.""" 110 111 error_status: str 112 """A Bungie short error status describes the cause of the error.""" 113 114 message_data: dict[str, str] 115 """A dict of string key, value that includes each cause of the error 116 to a message describes information about that error. 117 """ 118 119 def __str__(self) -> str: 120 status_name, status_value = ( 121 self.http_status.name.replace("_", "").title(), 122 self.http_status.value, 123 ) 124 return ( 125 f"{status_name}: " + "(" 126 f""" 127 http_status: {status_value}, 128 message: {self.message if self.message else 'UNDEFINED'}, 129 error_status: {self.error_status if self.error_status else 'UNDEFINED'}, 130 url: {self.url if self.url else 'UNDEFINED'}, 131 message_data: {self.message_data} 132 """ + ")" 133 )
An in-depth HTTP exception that's raised with more information.
2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data): 3 self.error_code = error_code 4 self.http_status = http_status 5 self.throttle_seconds = throttle_seconds 6 self.url = url 7 self.body = body 8 self.headers = headers 9 self.message = message 10 self.error_status = error_status 11 self.message_data = message_data 12 BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)
Method generated by attrs for class HTTPException.
A dict of string key, value that includes each cause of the error to a message describes information about that error.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
76@attrs.frozen(kw_only=True, weakref_slot=False) 77class Image: 78 """Representation of an image/avatar/picture Bungie resource. 79 80 Example 81 ------- 82 ```py 83 from aiobungie import Image 84 img = Image("img/destiny_content/pgcr/raid_eclipse.jpg") 85 print(img) 86 # https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg 87 88 # Save the image to a file. 89 await img.save("file_name", "/my/path/to/save/to", "jpeg") 90 ``` 91 92 Parameters 93 ---------- 94 path : `str | None` 95 The path to the image.. 96 """ 97 98 path: str = attrs.field() 99 100 @property 101 def is_missing(self) -> bool: 102 return not self.path 103 104 @property 105 def url(self) -> str: 106 """The URL to the image.""" 107 return self.create_url() 108 109 @staticmethod 110 def default() -> str: 111 """Returns the path to the missing Bungie image. 112 113 Note 114 ---- 115 This returns the path only, If you want an actual image object use `Image.default_or_else()` 116 """ 117 return "/img/misc/missing_icon_d2.png" 118 119 @classmethod 120 def default_or_else(cls, path: str | None = None) -> Self: 121 """Return the default image if `path` was `None` otherwise an `Image` object. 122 123 Example 124 ------- 125 ```py 126 img = Image.default_or_else(None) 127 print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png 128 129 img = Image.default_or_else("/some_path/image.png") 130 ``` 131 """ 132 return cls(path=path or Image.default()) 133 134 def create_url(self) -> str: 135 """Creates a full URL to the image path. 136 137 Returns 138 ------- 139 str 140 The URL to the image. 141 """ 142 return f"{url.BASE}/{self.path if self.path else self.default()}" 143 144 async def save( 145 self, 146 file_name: str, 147 path: pathlib.Path | str, 148 *, 149 mime_type: MimeType | str = MimeType.JPEG, 150 executor: concurrent.futures.Executor | None = None, 151 ) -> None: 152 """Saves the image to a file. 153 154 Parameters 155 ---------- 156 file_name : `str` 157 A name for the file to save the image to. 158 path : `pathlib.Path | str` 159 A path tp save the image to. 160 161 Other Parameters 162 ---------------- 163 mime_type : `MimeType | str` 164 MIME type of the image. Defaults to JPEG. 165 executor : `concurrent.futures.Executor | None` 166 An optional executor to use for writing the bytes of this image. 167 168 Raises 169 ------ 170 `FileNotFoundError` 171 If the path provided does not exist. 172 `RuntimeError` 173 If the image could not be saved. 174 `PermissionError` 175 If the path provided is not writable or does not have write permissions. 176 """ 177 if isinstance(path, pathlib.Path) and not path.exists(): 178 raise FileNotFoundError(f"File does not exist: {path!r}") 179 180 if self.is_missing: 181 return 182 183 path = pathlib.Path(path) 184 185 loop = helpers.get_or_make_loop() 186 187 try: 188 await loop.run_in_executor( 189 executor, _write, path, file_name, mime_type, await self.read() 190 ) 191 _LOGGER.info("Saved image to %s", file_name) 192 193 except asyncio.CancelledError: 194 pass 195 196 except Exception as err: 197 raise RuntimeError("Encountered an error while saving image.") from err 198 199 async def read(self) -> bytes: 200 """Read this image bytes. 201 202 Returns 203 ------- 204 `bytes` 205 The bytes of this image. 206 """ 207 client_session = aiohttp.ClientSession() 208 209 byte = b"" 210 try: 211 await client_session.__aenter__() 212 response = await client_session.get(self.create_url()) 213 214 if 300 >= response.status >= 200: 215 byte = await response.read() 216 217 except Exception as exc: 218 raise RuntimeError(f"Failed to read image: {exc}") from None 219 finally: 220 await client_session.__aexit__(None, None, None) 221 return byte 222 223 async def iter(self) -> collections.AsyncGenerator[bytes, None]: 224 """Iterates over the image bytes lazily. 225 226 Example 227 ------- 228 import aiobungie 229 230 resource = aiobungie.Image("img/misc/missing_icon_d2.png") 231 async for chunk in resource.iter(): 232 print(chunk) 233 234 Returns 235 ------- 236 `collections.AsyncGenerator[bytes, None]` 237 An async generator of the image bytes. 238 """ 239 240 async for chunk in self: 241 yield chunk 242 243 def __repr__(self) -> str: 244 return f"Image(url={self.create_url()})" 245 246 def __str__(self) -> str: 247 return self.create_url() 248 249 def __aiter__(self) -> Image: 250 return self 251 252 async def __anext__(self) -> bytes: 253 return await self.read() 254 255 def __await__(self) -> collections.Generator[None, None, bytes]: 256 return self.__anext__().__await__()
Representation of an image/avatar/picture Bungie resource.
Example
from aiobungie import Image
img = Image("img/destiny_content/pgcr/raid_eclipse.jpg")
print(img)
# https://www.bungie.net/img/destiny_content/pgcr/raid_eclipse.jpg
# Save the image to a file.
await img.save("file_name", "/my/path/to/save/to", "jpeg")
Parameters
- path (
str | None): The path to the image..
104 @property 105 def url(self) -> str: 106 """The URL to the image.""" 107 return self.create_url()
The URL to the image.
109 @staticmethod 110 def default() -> str: 111 """Returns the path to the missing Bungie image. 112 113 Note 114 ---- 115 This returns the path only, If you want an actual image object use `Image.default_or_else()` 116 """ 117 return "/img/misc/missing_icon_d2.png"
Returns the path to the missing Bungie image.
Note
This returns the path only, If you want an actual image object use Image.default_or_else()
119 @classmethod 120 def default_or_else(cls, path: str | None = None) -> Self: 121 """Return the default image if `path` was `None` otherwise an `Image` object. 122 123 Example 124 ------- 125 ```py 126 img = Image.default_or_else(None) 127 print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png 128 129 img = Image.default_or_else("/some_path/image.png") 130 ``` 131 """ 132 return cls(path=path or Image.default())
Return the default image if path was None otherwise an Image object.
Example
img = Image.default_or_else(None)
print(img.url()) # https://www.bungie.net/img/misc/missing_icon_d2.png
img = Image.default_or_else("/some_path/image.png")
134 def create_url(self) -> str: 135 """Creates a full URL to the image path. 136 137 Returns 138 ------- 139 str 140 The URL to the image. 141 """ 142 return f"{url.BASE}/{self.path if self.path else self.default()}"
Creates a full URL to the image path.
Returns
- str: The URL to the image.
144 async def save( 145 self, 146 file_name: str, 147 path: pathlib.Path | str, 148 *, 149 mime_type: MimeType | str = MimeType.JPEG, 150 executor: concurrent.futures.Executor | None = None, 151 ) -> None: 152 """Saves the image to a file. 153 154 Parameters 155 ---------- 156 file_name : `str` 157 A name for the file to save the image to. 158 path : `pathlib.Path | str` 159 A path tp save the image to. 160 161 Other Parameters 162 ---------------- 163 mime_type : `MimeType | str` 164 MIME type of the image. Defaults to JPEG. 165 executor : `concurrent.futures.Executor | None` 166 An optional executor to use for writing the bytes of this image. 167 168 Raises 169 ------ 170 `FileNotFoundError` 171 If the path provided does not exist. 172 `RuntimeError` 173 If the image could not be saved. 174 `PermissionError` 175 If the path provided is not writable or does not have write permissions. 176 """ 177 if isinstance(path, pathlib.Path) and not path.exists(): 178 raise FileNotFoundError(f"File does not exist: {path!r}") 179 180 if self.is_missing: 181 return 182 183 path = pathlib.Path(path) 184 185 loop = helpers.get_or_make_loop() 186 187 try: 188 await loop.run_in_executor( 189 executor, _write, path, file_name, mime_type, await self.read() 190 ) 191 _LOGGER.info("Saved image to %s", file_name) 192 193 except asyncio.CancelledError: 194 pass 195 196 except Exception as err: 197 raise RuntimeError("Encountered an error while saving image.") from err
Saves the image to a file.
Parameters
- file_name (
str): A name for the file to save the image to. - path (
pathlib.Path | str): A path tp save the image to.
Other Parameters
- mime_type (
MimeType | str): MIME type of the image. Defaults to JPEG. - executor (
concurrent.futures.Executor | None): An optional executor to use for writing the bytes of this image.
Raises
FileNotFoundError: If the path provided does not exist.RuntimeError: If the image could not be saved.PermissionError: If the path provided is not writable or does not have write permissions.
199 async def read(self) -> bytes: 200 """Read this image bytes. 201 202 Returns 203 ------- 204 `bytes` 205 The bytes of this image. 206 """ 207 client_session = aiohttp.ClientSession() 208 209 byte = b"" 210 try: 211 await client_session.__aenter__() 212 response = await client_session.get(self.create_url()) 213 214 if 300 >= response.status >= 200: 215 byte = await response.read() 216 217 except Exception as exc: 218 raise RuntimeError(f"Failed to read image: {exc}") from None 219 finally: 220 await client_session.__aexit__(None, None, None) 221 return byte
Read this image bytes.
Returns
bytes: The bytes of this image.
223 async def iter(self) -> collections.AsyncGenerator[bytes, None]: 224 """Iterates over the image bytes lazily. 225 226 Example 227 ------- 228 import aiobungie 229 230 resource = aiobungie.Image("img/misc/missing_icon_d2.png") 231 async for chunk in resource.iter(): 232 print(chunk) 233 234 Returns 235 ------- 236 `collections.AsyncGenerator[bytes, None]` 237 An async generator of the image bytes. 238 """ 239 240 async for chunk in self: 241 yield chunk
Iterates over the image bytes lazily.
Example
import aiobungie
resource = aiobungie.Image("img/misc/missing_icon_d2.png") async for chunk in resource.iter(): print(chunk)
Returns
collections.AsyncGenerator[bytes, None]: An async generator of the image bytes.
240@attrs.define(auto_exc=True) 241class InternalServerError(HTTPException): 242 """Raised for 5xx internal server errors."""
Raised for 5xx internal server errors.
2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data): 3 self.error_code = error_code 4 self.http_status = http_status 5 self.throttle_seconds = throttle_seconds 6 self.url = url 7 self.body = body 8 self.headers = headers 9 self.message = message 10 self.error_status = error_status 11 self.message_data = message_data 12 BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)
Method generated by attrs for class InternalServerError.
Inherited Members
- HTTPException
- error_code
- http_status
- throttle_seconds
- url
- body
- headers
- message
- error_status
- message_data
- builtins.BaseException
- with_traceback
- add_note
- args
711@typing.final 712class ItemBindStatus(int, Enum): 713 """An enum for Destiny 2 items bind status.""" 714 715 NOT_BOUND = 0 716 BOUND_TO_CHARACTER = 1 717 BOUND_TO_ACCOUNT = 2 718 BOUNT_TO_GUILD = 3
An enum for Destiny 2 items bind status.
721@typing.final 722class ItemLocation(int, Enum): 723 """An enum for Destiny 2 items location.""" 724 725 UNKNOWN = 0 726 INVENTORY = 1 727 VAULT = 2 728 VENDOR = 3 729 POSTMASTER = 4
An enum for Destiny 2 items location.
746@typing.final 747class ItemState(Flag): 748 """An enum for Destiny 2 item states.""" 749 750 NONE = 0 751 LOCKED = 1 << 0 752 TRACKED = 1 << 1 753 MASTERWORKED = 1 << 2 754 CRAFTED = 1 << 3 755 """If this bit is set, the item has been 'crafted' by the player.""" 756 HIGHLITED_OBJECTIVE = 1 << 4 757 """If this bit is set, the item is a 'highlighted' objective."""
An enum for Destiny 2 item states.
If this bit is set, the item is a 'highlighted' objective.
578@typing.final 579class ItemSubType(int, Enum): 580 """An enum for Destiny 2 inventory items subtype.""" 581 582 NONE = 0 583 AUTORIFLE = 6 584 SHOTGUN = 7 585 MACHINEGUN = 8 586 HANDCANNON = 9 587 ROCKETLAUNCHER = 10 588 FUSIONRIFLE = 11 589 SNIPERRIFLE = 12 590 PULSERIFLE = 13 591 SCOUTRIFLE = 14 592 SIDEARM = 17 593 SWORD = 18 594 MASK = 19 595 SHADER = 20 596 ORNAMENT = 21 597 FUSIONRIFLELINE = 22 598 GRENADELAUNCHER = 23 599 SUBMACHINEGUN = 24 600 TRACERIFLE = 25 601 HELMETARMOR = 26 602 GAUNTLETSARMOR = 27 603 CHESTARMOR = 28 604 LEGARMOR = 29 605 CLASSARMOR = 30 606 BOW = 31 607 DUMMYREPEATABLEBOUNTY = 32
An enum for Destiny 2 inventory items subtype.
610@typing.final 611class ItemTier(int, Enum): 612 """An enum for a Destiny 2 item tier.""" 613 614 NONE = 0 615 BASIC = 3340296461 616 COMMON = 2395677314 617 RARE = 2127292149 618 LEGENDERY = 4008398120 619 EXOTIC = 2759499571
An enum for a Destiny 2 item tier.
545@typing.final 546class ItemType(int, Enum): 547 """Enums for Destiny2's item types.""" 548 549 NONE = 0 550 CURRENCY = 1 551 ARMOR = 2 552 WEAPON = 3 553 MESSAGE = 7 554 ENGRAM = 8 555 CONSUMABLE = 9 556 EXCHANGEMATERIAL = 10 557 MISSIONREWARD = 11 558 QUESTSTEP = 12 559 QUESTSTEPCOMPLETE = 13 560 EMBLEM = 14 561 QUEST = 15 562 SUBCLASS = 16 563 CLANBANNER = 17 564 AURA = 18 565 MOD = 19 566 DUMMY = 20 567 SHIP = 21 568 VEHICLE = 22 569 EMOTE = 23 570 GHOST = 24 571 PACKAGE = 25 572 BOUNTY = 26 573 WRAPPER = 27 574 SEASONALARTIFACT = 28 575 FINISHER = 29
Enums for Destiny2's item types.
47class Iterator(typing.Generic[Item], collections.Iterator[Item]): 48 """A Flat, In-Memory iterator for sequenced based data. 49 50 Example 51 ------- 52 ```py 53 iterator = Iterator([1, 2, 3]) 54 55 # Map the results. 56 for item in iterator.map(lambda item: item * 2): 57 print(item) 58 # 2 59 # 4 60 61 # Indexing is also supported. 62 print(iterator[0]) 63 # 1 64 65 # Normal iteration. 66 for item in iterator: 67 print(item) 68 # 1 69 # 2 70 # 3 71 72 # Union two iterators. 73 iterator2 = Iterator([4, 5, 6]) 74 final = iterator | iterator2 75 # <Iterator([1, 2, 3, 4, 5, 6])> 76 ``` 77 78 Parameters 79 ---------- 80 items: `collections.Iterable[Item]` 81 The items to iterate over. 82 """ 83 84 __slots__ = ("_items",) 85 86 def __init__(self, items: collections.Iterable[Item]) -> None: 87 self._items = _builtins.iter(items) 88 89 @typing.overload 90 def collect(self) -> collections.Sequence[Item]: ... 91 92 @typing.overload 93 def collect(self, casting: _B) -> collections.Sequence[_B]: ... 94 95 def collect( 96 self, casting: _B | None = None 97 ) -> collections.Sequence[Item] | collections.Sequence[_B]: 98 """Collects all items in the iterator into an immutable collection. 99 100 Example 101 ------- 102 >>> iterator = Iterator([1, 2, 3]) 103 >>> iterator.collect(casting=str) 104 ("1", "2", "3") 105 106 Parameters 107 ---------- 108 casting: `T | None` 109 The type to cast the items to. If `None` is provided, the items will be returned as is. 110 111 Returns 112 ------- 113 `collections.Sequence[Item | T]` 114 An immutable sequence of the elements in the iterator. 115 116 Raises 117 ------ 118 `StopIteration` 119 If no elements are left in the iterator. 120 """ 121 if casting is not None: 122 return typing.cast( 123 collections.Sequence[_B], tuple(map(casting, self._items)) 124 ) 125 126 return tuple(self._items) 127 128 def copied(self) -> Iterator[Item]: 129 """Creates an iterator which `deeply` copies all of its elements. 130 131 .. warn:: 132 This will `deeply` copy all of the elements, Use `Iterator.by_ref` 133 if you want copy of the iterator reference. 134 135 Example 136 ------- 137 ```py 138 it = Iterator([None, None, None]) 139 copied_iter = it.copied() 140 assert it.collect() == copied.collect() 141 ``` 142 """ 143 return Iterator(_copy.deepcopy(self._items)) 144 145 def by_ref(self) -> Iterator[Item]: 146 """Creates an iterator which doesn't consume its elements. 147 but instead shallow copy it. 148 149 Example 150 ------- 151 ```py 152 it = Iterator([None, None, None]) 153 for ref in it.by_ref(): 154 ... 155 156 # Original not consumed. 157 assert it.count() == 3 158 ``` 159 """ 160 return Iterator(_copy.copy(self._items)) 161 162 def next(self) -> Item: 163 """Returns the next item in the iterator. 164 165 Example 166 ------- 167 ```py 168 iterator = Iterator(["1", "2", "3"]) 169 item = iterator.next() 170 assert item == "1" 171 item = iterator.next() 172 assert item == "2" 173 ``` 174 175 Raises 176 ------ 177 `StopIteration` 178 If no elements are left in the iterator. 179 """ 180 try: 181 return self.__next__() 182 except StopIteration: 183 self._ok() 184 185 def map( 186 self, predicate: collections.Callable[[Item], OtherItem] 187 ) -> Iterator[OtherItem]: 188 """Maps each item in the iterator to its predicated value. 189 190 Example 191 ------- 192 ```py 193 iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value)) 194 print(iterator) 195 # <Iterator([1, 2, 3])> 196 ``` 197 198 Parameters 199 ---------- 200 predicate: `collections.Callable[[Item], OtherItem]` 201 The function to map each item in the iterator to its predicated value. 202 203 Returns 204 ------- 205 `Iterator[OtherItem]` 206 The mapped iterator. 207 208 Raises 209 ------ 210 `StopIteration` 211 If no elements are left in the iterator. 212 """ 213 return Iterator(map(predicate, self._items)) 214 215 def take(self, n: int) -> Iterator[Item]: 216 """Take the first number of items until the number of items are yielded or 217 the end of the iterator is reached. 218 219 Example 220 ------- 221 ```py 222 iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT]) 223 print(iterator.take(2)) 224 # <Iterator([GameMode.RAID, GameMode.STRIKE])> 225 ``` 226 227 Parameters 228 ---------- 229 n: `int` 230 The number of items to take. 231 232 Raises 233 ------ 234 `StopIteration` 235 If no elements are left in the iterator. 236 """ 237 return Iterator(itertools.islice(self._items, n)) 238 239 def take_while( 240 self, predicate: collections.Callable[[Item], bool] 241 ) -> Iterator[Item]: 242 """Yields items from the iterator while predicate returns `True`. 243 244 Example 245 ------- 246 ```py 247 iterator = Iterator([STEAM, XBOX, STADIA]) 248 print(iterator.take_while(lambda platform: platform is not XBOX)) 249 # <Iterator([STEAM])> 250 ``` 251 252 Parameters 253 ---------- 254 predicate: `collections.Callable[[Item], bool]` 255 The function to predicate each item in the iterator. 256 257 Raises 258 ------ 259 `StopIteration` 260 If no elements are left in the iterator. 261 """ 262 return Iterator(itertools.takewhile(predicate, self._items)) 263 264 def drop_while( 265 self, predicate: collections.Callable[[Item], bool] 266 ) -> Iterator[Item]: 267 """Yields items from the iterator while predicate returns `False`. 268 269 Example 270 ------- 271 ```py 272 iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")]) 273 print(iterator.drop_while(lambda membership: membership.name is not "Jim")) 274 # <Iterator([DestinyMembership(name="Bob")])> 275 ``` 276 277 Parameters 278 ---------- 279 predicate: `collections.Callable[[Item], bool]` 280 The function to predicate each item in the iterator. 281 282 Raises 283 ------ 284 `StopIteration` 285 If no elements are left in the iterator. 286 """ 287 return Iterator(itertools.dropwhile(predicate, self._items)) 288 289 def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]: 290 """Filters the iterator to only yield items that match the predicate. 291 292 Example 293 ------- 294 ```py 295 names = Iterator(["Jim", "Bob", "Mike", "Jess"]) 296 print(names.filter(lambda n: n != "Jim")) 297 # <Iterator(["Bob", "Mike", "Jess"])> 298 ``` 299 """ 300 return Iterator(filter(predicate, self._items)) 301 302 def skip(self, n: int) -> Iterator[Item]: 303 """Skips the first number of items in the iterator. 304 305 Example 306 ------- 307 ```py 308 iterator = Iterator([STEAM, XBOX, STADIA]) 309 print(iterator.skip(1)) 310 # <Iterator([XBOX, STADIA])> 311 ``` 312 """ 313 return Iterator(itertools.islice(self._items, n, None)) 314 315 def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]: 316 """Zips the iterator with another iterable. 317 318 Example 319 ------- 320 ```py 321 iterator = Iterator([1, 3, 5]) 322 other = Iterator([2, 4, 6]) 323 for item, other_item in iterator.zip(other): 324 print(item, other_item) 325 # <Iterator([(1, 2), (3, 4), (5, 6)])> 326 ``` 327 328 Parameters 329 ---------- 330 other: `Iterator[OtherItem]` 331 The iterable to zip with. 332 333 Raises 334 ------ 335 `StopIteration` 336 If no elements are left in the iterator. 337 """ 338 return Iterator(zip(self._items, other)) 339 340 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 341 """`True` if all items in the iterator match the predicate. 342 343 Example 344 ------- 345 ```py 346 iterator = Iterator([1, 2, 3]) 347 while iterator.all(lambda item: isinstance(item, int)): 348 print("Still all integers") 349 continue 350 # Still all integers 351 ``` 352 353 Parameters 354 ---------- 355 predicate: `collections.Callable[[Item], bool]` 356 The function to test each item in the iterator. 357 358 Raises 359 ------ 360 `StopIteration` 361 If no elements are left in the iterator. 362 """ 363 return all(predicate(item) for item in self) 364 365 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 366 """`True` if any items in the iterator match the predicate. 367 368 Example 369 ------- 370 ```py 371 iterator = Iterator([1, 2, 3]) 372 if iterator.any(lambda item: isinstance(item, int)): 373 print("At least one item is an int.") 374 # At least one item is an int. 375 ``` 376 377 Parameters 378 ---------- 379 predicate: `collections.Callable[[Item], bool]` 380 The function to test each item in the iterator. 381 382 Raises 383 ------ 384 `StopIteration` 385 If no elements are left in the iterator. 386 """ 387 return any(predicate(item) for item in self) 388 389 def sort( 390 self, 391 *, 392 key: collections.Callable[[Item], typeshed.SupportsRichComparison], 393 reverse: bool = False, 394 ) -> Iterator[Item]: 395 """Sorts the iterator. 396 397 Example 398 ------- 399 ```py 400 iterator = Iterator([3, 1, 6, 7]) 401 print(iterator.sort(key=lambda item: item)) 402 # <Iterator([1, 3, 6, 7])> 403 ``` 404 405 Parameters 406 ---------- 407 key: `collections.Callable[[Item], Any]` 408 The function to sort by. 409 reverse: `bool` 410 Whether to reverse the sort. 411 412 Raises 413 ------ 414 `StopIteration` 415 If no elements are left in the iterator. 416 """ 417 return Iterator(sorted(self._items, key=key, reverse=reverse)) 418 419 def first(self) -> Item: 420 """Returns the first item in the iterator. 421 422 Example 423 ------- 424 ```py 425 iterator = Iterator([3, 1, 6, 7]) 426 print(iterator.first()) 427 3 428 ``` 429 430 Raises 431 ------ 432 `StopIteration` 433 If no elements are left in the iterator. 434 """ 435 return self.take(1).next() 436 437 def last(self) -> Item: 438 """Returns the last item in the iterator. 439 440 Example 441 ------ 442 ```py 443 it = Iterator((1, 2, 3)) 444 assert it.first() == 1 and it.last() == 3 445 ``` 446 """ 447 return self.reversed().first() 448 449 def reversed(self) -> Iterator[Item]: 450 """Returns a new iterator that yields the items in the iterator in reverse order. 451 452 Example 453 ------- 454 ```py 455 iterator = Iterator([3, 1, 6, 7]) 456 print(iterator.reversed()) 457 # <Iterator([7, 6, 1, 3])> 458 ``` 459 460 Raises 461 ------ 462 `StopIteration` 463 If no elements are left in the iterator. 464 """ 465 return Iterator(reversed(self.collect())) 466 467 def count(self) -> int: 468 """Returns the number of items in the iterator. 469 470 Example 471 ------- 472 ```py 473 iterator = Iterator([3, 1, 6, 7]) 474 print(iterator.count()) 475 4 476 ``` 477 """ 478 count = 0 479 for _ in self: 480 count += 1 481 482 return count 483 484 def union(self, other: Iterator[Item]) -> Iterator[Item]: 485 """Returns a new iterator that yields all items from both iterators. 486 487 Example 488 ------- 489 ```py 490 iterator = Iterator([1, 2, 3]) 491 other = Iterator([4, 5, 6]) 492 print(iterator.union(other)) 493 # <Iterator([1, 2, 3, 4, 5, 6])> 494 ``` 495 496 Parameters 497 ---------- 498 other: `Iterator[Item]` 499 The iterable to union with. 500 501 Raises 502 ------ 503 `StopIteration` 504 If no elements are left in the iterator. 505 """ 506 return Iterator(itertools.chain(self._items, other)) 507 508 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 509 """Calls the function on each item in the iterator. 510 511 Example 512 ------- 513 ```py 514 iterator = Iterator([1, 2, 3]) 515 iterator.for_each(lambda item: print(item)) 516 # 1 517 # 2 518 # 3 519 ``` 520 521 Parameters 522 ---------- 523 func: `typeshed.Callable[[Item], None]` 524 The function to call on each item in the iterator. 525 """ 526 for item in self: 527 func(item) 528 529 async def async_for_each( 530 self, 531 func: collections.Callable[[Item], collections.Coroutine[None, None, None]], 532 ) -> None: 533 """Calls the async function on each item in the iterator concurrently. 534 535 Example 536 ------- 537 ```py 538 async def signup(username: str) -> None: 539 async with aiohttp.request('POST', '...') as r: 540 # Actual logic. 541 ... 542 543 async def main(): 544 users = aiobungie.into_iter(["user_danny", "user_jojo"]) 545 await users.async_for_each(lambda username: signup(username)) 546 ``` 547 548 Parameters 549 ---------- 550 func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]` 551 The async function to call on each item in the iterator. 552 """ 553 await _helpers.awaits(*(func(item) for item in self)) 554 555 def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]: 556 """Returns a new iterator that yields tuples of the index and item. 557 558 Example 559 ------- 560 ```py 561 iterator = Iterator([1, 2, 3]) 562 for index, item in iterator.enumerate(): 563 print(index, item) 564 # 0 1 565 # 1 2 566 # 2 3 567 ``` 568 569 Raises 570 ------ 571 `StopIteration` 572 If no elements are left in the iterator. 573 """ 574 return Iterator(enumerate(self._items, start=start)) 575 576 def _ok(self) -> typing.NoReturn: 577 raise StopIteration("No more items in the iterator.") from None 578 579 def __getitem__(self, index: int) -> Item: 580 try: 581 return self.skip(index).first() 582 except IndexError: 583 self._ok() 584 585 def __or__(self, other: Iterator[Item]) -> Iterator[Item]: 586 return self.union(other) 587 588 # This is a never. 589 def __setitem__(self) -> typing.NoReturn: 590 raise TypeError( 591 f"{type(self).__name__} doesn't support item assignment." 592 ) from None 593 594 def __repr__(self) -> str: 595 return f"Iterator(ptr: {hex(id(self._items))})" 596 597 __str__ = __repr__ 598 599 def __len__(self) -> int: 600 return self.count() 601 602 def __iter__(self) -> Iterator[Item]: 603 return self 604 605 def __next__(self) -> Item: 606 try: 607 item = next(self._items) 608 except StopIteration: 609 self._ok() 610 611 return item
A Flat, In-Memory iterator for sequenced based data.
Example
iterator = Iterator([1, 2, 3])
# Map the results.
for item in iterator.map(lambda item: item * 2):
print(item)
# 2
# 4
# Indexing is also supported.
print(iterator[0])
# 1
# Normal iteration.
for item in iterator:
print(item)
# 1
# 2
# 3
# Union two iterators.
iterator2 = Iterator([4, 5, 6])
final = iterator | iterator2
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
- items (
collections.Iterable[Item]): The items to iterate over.
95 def collect( 96 self, casting: _B | None = None 97 ) -> collections.Sequence[Item] | collections.Sequence[_B]: 98 """Collects all items in the iterator into an immutable collection. 99 100 Example 101 ------- 102 >>> iterator = Iterator([1, 2, 3]) 103 >>> iterator.collect(casting=str) 104 ("1", "2", "3") 105 106 Parameters 107 ---------- 108 casting: `T | None` 109 The type to cast the items to. If `None` is provided, the items will be returned as is. 110 111 Returns 112 ------- 113 `collections.Sequence[Item | T]` 114 An immutable sequence of the elements in the iterator. 115 116 Raises 117 ------ 118 `StopIteration` 119 If no elements are left in the iterator. 120 """ 121 if casting is not None: 122 return typing.cast( 123 collections.Sequence[_B], tuple(map(casting, self._items)) 124 ) 125 126 return tuple(self._items)
Collects all items in the iterator into an immutable collection.
Example
>>> iterator = Iterator([1, 2, 3])
>>> iterator.collect(casting=str)
("1", "2", "3")
Parameters
- casting (
T | None): The type to cast the items to. IfNoneis provided, the items will be returned as is.
Returns
collections.Sequence[Item | T]: An immutable sequence of the elements in the iterator.
Raises
StopIteration: If no elements are left in the iterator.
128 def copied(self) -> Iterator[Item]: 129 """Creates an iterator which `deeply` copies all of its elements. 130 131 .. warn:: 132 This will `deeply` copy all of the elements, Use `Iterator.by_ref` 133 if you want copy of the iterator reference. 134 135 Example 136 ------- 137 ```py 138 it = Iterator([None, None, None]) 139 copied_iter = it.copied() 140 assert it.collect() == copied.collect() 141 ``` 142 """ 143 return Iterator(_copy.deepcopy(self._items))
Creates an iterator which deeply copies all of its elements.
.. warn::
This will deeply copy all of the elements, Use Iterator.by_ref
if you want copy of the iterator reference.
Example
it = Iterator([None, None, None])
copied_iter = it.copied()
assert it.collect() == copied.collect()
145 def by_ref(self) -> Iterator[Item]: 146 """Creates an iterator which doesn't consume its elements. 147 but instead shallow copy it. 148 149 Example 150 ------- 151 ```py 152 it = Iterator([None, None, None]) 153 for ref in it.by_ref(): 154 ... 155 156 # Original not consumed. 157 assert it.count() == 3 158 ``` 159 """ 160 return Iterator(_copy.copy(self._items))
Creates an iterator which doesn't consume its elements. but instead shallow copy it.
Example
it = Iterator([None, None, None])
for ref in it.by_ref():
...
# Original not consumed.
assert it.count() == 3
162 def next(self) -> Item: 163 """Returns the next item in the iterator. 164 165 Example 166 ------- 167 ```py 168 iterator = Iterator(["1", "2", "3"]) 169 item = iterator.next() 170 assert item == "1" 171 item = iterator.next() 172 assert item == "2" 173 ``` 174 175 Raises 176 ------ 177 `StopIteration` 178 If no elements are left in the iterator. 179 """ 180 try: 181 return self.__next__() 182 except StopIteration: 183 self._ok()
Returns the next item in the iterator.
Example
iterator = Iterator(["1", "2", "3"])
item = iterator.next()
assert item == "1"
item = iterator.next()
assert item == "2"
Raises
StopIteration: If no elements are left in the iterator.
185 def map( 186 self, predicate: collections.Callable[[Item], OtherItem] 187 ) -> Iterator[OtherItem]: 188 """Maps each item in the iterator to its predicated value. 189 190 Example 191 ------- 192 ```py 193 iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value)) 194 print(iterator) 195 # <Iterator([1, 2, 3])> 196 ``` 197 198 Parameters 199 ---------- 200 predicate: `collections.Callable[[Item], OtherItem]` 201 The function to map each item in the iterator to its predicated value. 202 203 Returns 204 ------- 205 `Iterator[OtherItem]` 206 The mapped iterator. 207 208 Raises 209 ------ 210 `StopIteration` 211 If no elements are left in the iterator. 212 """ 213 return Iterator(map(predicate, self._items))
Maps each item in the iterator to its predicated value.
Example
iterator = Iterator(["1", "2", "3"]).map(lambda value: int(value))
print(iterator)
# <Iterator([1, 2, 3])>
Parameters
- predicate (
collections.Callable[[Item], OtherItem]): The function to map each item in the iterator to its predicated value.
Returns
Iterator[OtherItem]: The mapped iterator.
Raises
StopIteration: If no elements are left in the iterator.
215 def take(self, n: int) -> Iterator[Item]: 216 """Take the first number of items until the number of items are yielded or 217 the end of the iterator is reached. 218 219 Example 220 ------- 221 ```py 222 iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT]) 223 print(iterator.take(2)) 224 # <Iterator([GameMode.RAID, GameMode.STRIKE])> 225 ``` 226 227 Parameters 228 ---------- 229 n: `int` 230 The number of items to take. 231 232 Raises 233 ------ 234 `StopIteration` 235 If no elements are left in the iterator. 236 """ 237 return Iterator(itertools.islice(self._items, n))
Take the first number of items until the number of items are yielded or the end of the iterator is reached.
Example
iterator = Iterator([GameMode.RAID, GameMode.STRIKE, GameMode.GAMBIT])
print(iterator.take(2))
# <Iterator([GameMode.RAID, GameMode.STRIKE])>
Parameters
- n (
int): The number of items to take.
Raises
StopIteration: If no elements are left in the iterator.
239 def take_while( 240 self, predicate: collections.Callable[[Item], bool] 241 ) -> Iterator[Item]: 242 """Yields items from the iterator while predicate returns `True`. 243 244 Example 245 ------- 246 ```py 247 iterator = Iterator([STEAM, XBOX, STADIA]) 248 print(iterator.take_while(lambda platform: platform is not XBOX)) 249 # <Iterator([STEAM])> 250 ``` 251 252 Parameters 253 ---------- 254 predicate: `collections.Callable[[Item], bool]` 255 The function to predicate each item in the iterator. 256 257 Raises 258 ------ 259 `StopIteration` 260 If no elements are left in the iterator. 261 """ 262 return Iterator(itertools.takewhile(predicate, self._items))
Yields items from the iterator while predicate returns True.
Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.take_while(lambda platform: platform is not XBOX))
# <Iterator([STEAM])>
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
StopIteration: If no elements are left in the iterator.
264 def drop_while( 265 self, predicate: collections.Callable[[Item], bool] 266 ) -> Iterator[Item]: 267 """Yields items from the iterator while predicate returns `False`. 268 269 Example 270 ------- 271 ```py 272 iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")]) 273 print(iterator.drop_while(lambda membership: membership.name is not "Jim")) 274 # <Iterator([DestinyMembership(name="Bob")])> 275 ``` 276 277 Parameters 278 ---------- 279 predicate: `collections.Callable[[Item], bool]` 280 The function to predicate each item in the iterator. 281 282 Raises 283 ------ 284 `StopIteration` 285 If no elements are left in the iterator. 286 """ 287 return Iterator(itertools.dropwhile(predicate, self._items))
Yields items from the iterator while predicate returns False.
Example
iterator = Iterator([DestinyMembership(name="Jim"), DestinyMembership(name="Bob")])
print(iterator.drop_while(lambda membership: membership.name is not "Jim"))
# <Iterator([DestinyMembership(name="Bob")])>
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to predicate each item in the iterator.
Raises
StopIteration: If no elements are left in the iterator.
289 def filter(self, predicate: collections.Callable[[Item], bool]) -> Iterator[Item]: 290 """Filters the iterator to only yield items that match the predicate. 291 292 Example 293 ------- 294 ```py 295 names = Iterator(["Jim", "Bob", "Mike", "Jess"]) 296 print(names.filter(lambda n: n != "Jim")) 297 # <Iterator(["Bob", "Mike", "Jess"])> 298 ``` 299 """ 300 return Iterator(filter(predicate, self._items))
Filters the iterator to only yield items that match the predicate.
Example
names = Iterator(["Jim", "Bob", "Mike", "Jess"])
print(names.filter(lambda n: n != "Jim"))
# <Iterator(["Bob", "Mike", "Jess"])>
302 def skip(self, n: int) -> Iterator[Item]: 303 """Skips the first number of items in the iterator. 304 305 Example 306 ------- 307 ```py 308 iterator = Iterator([STEAM, XBOX, STADIA]) 309 print(iterator.skip(1)) 310 # <Iterator([XBOX, STADIA])> 311 ``` 312 """ 313 return Iterator(itertools.islice(self._items, n, None))
Skips the first number of items in the iterator.
Example
iterator = Iterator([STEAM, XBOX, STADIA])
print(iterator.skip(1))
# <Iterator([XBOX, STADIA])>
315 def zip(self, other: Iterator[OtherItem]) -> Iterator[tuple[Item, OtherItem]]: 316 """Zips the iterator with another iterable. 317 318 Example 319 ------- 320 ```py 321 iterator = Iterator([1, 3, 5]) 322 other = Iterator([2, 4, 6]) 323 for item, other_item in iterator.zip(other): 324 print(item, other_item) 325 # <Iterator([(1, 2), (3, 4), (5, 6)])> 326 ``` 327 328 Parameters 329 ---------- 330 other: `Iterator[OtherItem]` 331 The iterable to zip with. 332 333 Raises 334 ------ 335 `StopIteration` 336 If no elements are left in the iterator. 337 """ 338 return Iterator(zip(self._items, other))
Zips the iterator with another iterable.
Example
iterator = Iterator([1, 3, 5])
other = Iterator([2, 4, 6])
for item, other_item in iterator.zip(other):
print(item, other_item)
# <Iterator([(1, 2), (3, 4), (5, 6)])>
Parameters
- other (
Iterator[OtherItem]): The iterable to zip with.
Raises
StopIteration: If no elements are left in the iterator.
340 def all(self, predicate: collections.Callable[[Item], bool]) -> bool: 341 """`True` if all items in the iterator match the predicate. 342 343 Example 344 ------- 345 ```py 346 iterator = Iterator([1, 2, 3]) 347 while iterator.all(lambda item: isinstance(item, int)): 348 print("Still all integers") 349 continue 350 # Still all integers 351 ``` 352 353 Parameters 354 ---------- 355 predicate: `collections.Callable[[Item], bool]` 356 The function to test each item in the iterator. 357 358 Raises 359 ------ 360 `StopIteration` 361 If no elements are left in the iterator. 362 """ 363 return all(predicate(item) for item in self)
True if all items in the iterator match the predicate.
Example
iterator = Iterator([1, 2, 3])
while iterator.all(lambda item: isinstance(item, int)):
print("Still all integers")
continue
# Still all integers
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
StopIteration: If no elements are left in the iterator.
365 def any(self, predicate: collections.Callable[[Item], bool]) -> bool: 366 """`True` if any items in the iterator match the predicate. 367 368 Example 369 ------- 370 ```py 371 iterator = Iterator([1, 2, 3]) 372 if iterator.any(lambda item: isinstance(item, int)): 373 print("At least one item is an int.") 374 # At least one item is an int. 375 ``` 376 377 Parameters 378 ---------- 379 predicate: `collections.Callable[[Item], bool]` 380 The function to test each item in the iterator. 381 382 Raises 383 ------ 384 `StopIteration` 385 If no elements are left in the iterator. 386 """ 387 return any(predicate(item) for item in self)
True if any items in the iterator match the predicate.
Example
iterator = Iterator([1, 2, 3])
if iterator.any(lambda item: isinstance(item, int)):
print("At least one item is an int.")
# At least one item is an int.
Parameters
- predicate (
collections.Callable[[Item], bool]): The function to test each item in the iterator.
Raises
StopIteration: If no elements are left in the iterator.
389 def sort( 390 self, 391 *, 392 key: collections.Callable[[Item], typeshed.SupportsRichComparison], 393 reverse: bool = False, 394 ) -> Iterator[Item]: 395 """Sorts the iterator. 396 397 Example 398 ------- 399 ```py 400 iterator = Iterator([3, 1, 6, 7]) 401 print(iterator.sort(key=lambda item: item)) 402 # <Iterator([1, 3, 6, 7])> 403 ``` 404 405 Parameters 406 ---------- 407 key: `collections.Callable[[Item], Any]` 408 The function to sort by. 409 reverse: `bool` 410 Whether to reverse the sort. 411 412 Raises 413 ------ 414 `StopIteration` 415 If no elements are left in the iterator. 416 """ 417 return Iterator(sorted(self._items, key=key, reverse=reverse))
Sorts the iterator.
Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.sort(key=lambda item: item))
# <Iterator([1, 3, 6, 7])>
Parameters
- key (
collections.Callable[[Item], Any]): The function to sort by. - reverse (
bool): Whether to reverse the sort.
Raises
StopIteration: If no elements are left in the iterator.
419 def first(self) -> Item: 420 """Returns the first item in the iterator. 421 422 Example 423 ------- 424 ```py 425 iterator = Iterator([3, 1, 6, 7]) 426 print(iterator.first()) 427 3 428 ``` 429 430 Raises 431 ------ 432 `StopIteration` 433 If no elements are left in the iterator. 434 """ 435 return self.take(1).next()
Returns the first item in the iterator.
Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.first())
3
Raises
StopIteration: If no elements are left in the iterator.
437 def last(self) -> Item: 438 """Returns the last item in the iterator. 439 440 Example 441 ------ 442 ```py 443 it = Iterator((1, 2, 3)) 444 assert it.first() == 1 and it.last() == 3 445 ``` 446 """ 447 return self.reversed().first()
Returns the last item in the iterator.
Example
it = Iterator((1, 2, 3))
assert it.first() == 1 and it.last() == 3
449 def reversed(self) -> Iterator[Item]: 450 """Returns a new iterator that yields the items in the iterator in reverse order. 451 452 Example 453 ------- 454 ```py 455 iterator = Iterator([3, 1, 6, 7]) 456 print(iterator.reversed()) 457 # <Iterator([7, 6, 1, 3])> 458 ``` 459 460 Raises 461 ------ 462 `StopIteration` 463 If no elements are left in the iterator. 464 """ 465 return Iterator(reversed(self.collect()))
Returns a new iterator that yields the items in the iterator in reverse order.
Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.reversed())
# <Iterator([7, 6, 1, 3])>
Raises
StopIteration: If no elements are left in the iterator.
467 def count(self) -> int: 468 """Returns the number of items in the iterator. 469 470 Example 471 ------- 472 ```py 473 iterator = Iterator([3, 1, 6, 7]) 474 print(iterator.count()) 475 4 476 ``` 477 """ 478 count = 0 479 for _ in self: 480 count += 1 481 482 return count
Returns the number of items in the iterator.
Example
iterator = Iterator([3, 1, 6, 7])
print(iterator.count())
4
484 def union(self, other: Iterator[Item]) -> Iterator[Item]: 485 """Returns a new iterator that yields all items from both iterators. 486 487 Example 488 ------- 489 ```py 490 iterator = Iterator([1, 2, 3]) 491 other = Iterator([4, 5, 6]) 492 print(iterator.union(other)) 493 # <Iterator([1, 2, 3, 4, 5, 6])> 494 ``` 495 496 Parameters 497 ---------- 498 other: `Iterator[Item]` 499 The iterable to union with. 500 501 Raises 502 ------ 503 `StopIteration` 504 If no elements are left in the iterator. 505 """ 506 return Iterator(itertools.chain(self._items, other))
Returns a new iterator that yields all items from both iterators.
Example
iterator = Iterator([1, 2, 3])
other = Iterator([4, 5, 6])
print(iterator.union(other))
# <Iterator([1, 2, 3, 4, 5, 6])>
Parameters
- other (
Iterator[Item]): The iterable to union with.
Raises
StopIteration: If no elements are left in the iterator.
508 def for_each(self, func: collections.Callable[[Item], typing.Any]) -> None: 509 """Calls the function on each item in the iterator. 510 511 Example 512 ------- 513 ```py 514 iterator = Iterator([1, 2, 3]) 515 iterator.for_each(lambda item: print(item)) 516 # 1 517 # 2 518 # 3 519 ``` 520 521 Parameters 522 ---------- 523 func: `typeshed.Callable[[Item], None]` 524 The function to call on each item in the iterator. 525 """ 526 for item in self: 527 func(item)
Calls the function on each item in the iterator.
Example
iterator = Iterator([1, 2, 3])
iterator.for_each(lambda item: print(item))
# 1
# 2
# 3
Parameters
- func (
typeshed.Callable[[Item], None]): The function to call on each item in the iterator.
529 async def async_for_each( 530 self, 531 func: collections.Callable[[Item], collections.Coroutine[None, None, None]], 532 ) -> None: 533 """Calls the async function on each item in the iterator concurrently. 534 535 Example 536 ------- 537 ```py 538 async def signup(username: str) -> None: 539 async with aiohttp.request('POST', '...') as r: 540 # Actual logic. 541 ... 542 543 async def main(): 544 users = aiobungie.into_iter(["user_danny", "user_jojo"]) 545 await users.async_for_each(lambda username: signup(username)) 546 ``` 547 548 Parameters 549 ---------- 550 func: `collections.Callable[[Item], collections.Coroutine[None, None, None]]` 551 The async function to call on each item in the iterator. 552 """ 553 await _helpers.awaits(*(func(item) for item in self))
Calls the async function on each item in the iterator concurrently.
Example
async def signup(username: str) -> None:
async with aiohttp.request('POST', '...') as r:
# Actual logic.
...
async def main():
users = aiobungie.into_iter(["user_danny", "user_jojo"])
await users.async_for_each(lambda username: signup(username))
Parameters
- func (
collections.Callable[[Item], collections.Coroutine[None, None, None]]): The async function to call on each item in the iterator.
555 def enumerate(self, *, start: int = 0) -> Iterator[tuple[int, Item]]: 556 """Returns a new iterator that yields tuples of the index and item. 557 558 Example 559 ------- 560 ```py 561 iterator = Iterator([1, 2, 3]) 562 for index, item in iterator.enumerate(): 563 print(index, item) 564 # 0 1 565 # 1 2 566 # 2 3 567 ``` 568 569 Raises 570 ------ 571 `StopIteration` 572 If no elements are left in the iterator. 573 """ 574 return Iterator(enumerate(self._items, start=start))
Returns a new iterator that yields tuples of the index and item.
Example
iterator = Iterator([1, 2, 3])
for index, item in iterator.enumerate():
print(index, item)
# 0 1
# 1 2
# 2 3
Raises
StopIteration: If no elements are left in the iterator.
702@typing.final 703class MembershipOption(int, Enum): 704 """A enum for GroupV2 membership options.""" 705 706 REVIEWD = 0 707 OPEN = 1 708 CLOSED = 2
A enum for GroupV2 membership options.
450@typing.final 451class MembershipType(int, Enum): 452 """An Enum for Bungie membership types.""" 453 454 NONE = 0 455 XBOX = 1 456 PSN = 2 457 STEAM = 3 458 BLIZZARD = 4 459 STADIA = 5 460 EPIC_GAMES_STORE = 6 461 DEMON = 10 462 BUNGIE = 254 463 ALL = -1
An Enum for Bungie membership types.
181@attrs.define(auto_exc=True) 182class MembershipTypeError(BadRequest): 183 """A bad request error raised when passing wrong membership to the request. 184 185 Those fields are useful since it returns the correct membership and id which can be used 186 to make the request again with those fields. 187 188 Example 189 ------- 190 ```py 191 try: 192 profile = await client.fetch_profile( 193 member_id=1, 194 type=aiobungie.MembershipType.STADIA, 195 components=() 196 ) 197 198 # Membership type is wrong! 199 except aiobungie.MembershipTypeError as err: 200 correct_membership = err.into_membership() 201 profile_id = err.membership_id 202 203 # Recall the method. 204 profile = await client.fetch_profile( 205 member_id=profile_id, 206 type=correct_membership, 207 components=() 208 ) 209 ``` 210 """ 211 212 membership_type: str 213 """The errored membership type passed to the request.""" 214 215 membership_id: int 216 """The errored user's membership id.""" 217 218 required_membership: str 219 """The required correct membership for errored user.""" 220 221 def into_membership(self, value: str | None = None) -> enums.MembershipType: 222 """Turn the required membership from `str` into `aiobungie.Membership` type. 223 224 If value parameter is not provided it will fall back to the required membership. 225 """ 226 if value is None: 227 return _MEMBERSHIP_LOOKUP[self.required_membership] 228 return _MEMBERSHIP_LOOKUP[value] 229 230 def __str__(self) -> str: 231 return ( 232 f"Expected membership: {self.into_membership().name.replace('_', '').title()}, " 233 f"But got {self.into_membership(self.membership_type)} for id {self.membership_id}" 234 ) 235 236 def __int__(self) -> int: 237 return int(self.membership_id)
A bad request error raised when passing wrong membership to the request.
Those fields are useful since it returns the correct membership and id which can be used to make the request again with those fields.
Example
try:
profile = await client.fetch_profile(
member_id=1,
type=aiobungie.MembershipType.STADIA,
components=()
)
# Membership type is wrong!
except aiobungie.MembershipTypeError as err:
correct_membership = err.into_membership()
profile_id = err.membership_id
# Recall the method.
profile = await client.fetch_profile(
member_id=profile_id,
type=correct_membership,
components=()
)
2def __init__(self, message, url, body, headers, membership_type, membership_id, required_membership): 3 self.message = message 4 self.url = url 5 self.body = body 6 self.headers = headers 7 self.http_status = attr_dict['http_status'].default 8 self.membership_type = membership_type 9 self.membership_id = membership_id 10 self.required_membership = required_membership 11 BaseException.__init__(self, self.message,self.url,self.body,self.headers,self.membership_type,self.membership_id,self.required_membership)
Method generated by attrs for class MembershipTypeError.
221 def into_membership(self, value: str | None = None) -> enums.MembershipType: 222 """Turn the required membership from `str` into `aiobungie.Membership` type. 223 224 If value parameter is not provided it will fall back to the required membership. 225 """ 226 if value is None: 227 return _MEMBERSHIP_LOOKUP[self.required_membership] 228 return _MEMBERSHIP_LOOKUP[value]
Turn the required membership from str into aiobungie.Membership type.
If value parameter is not provided it will fall back to the required membership.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
495@typing.final 496class MilestoneType(int, Enum): 497 """An Enum for Destiny 2 milestone types.""" 498 499 UNKNOWN = 0 500 TUTORIAL = 1 501 ONETIME = 2 502 WEEKLY = 3 503 DAILY = 4 504 SPECIAL = 5
An Enum for Destiny 2 milestone types.
145@attrs.define(auto_exc=True) 146class NotFound(HTTPException): 147 """Raised when an unknown resource was not found.""" 148 149 http_status: http.HTTPStatus = attrs.field( 150 default=http.HTTPStatus.NOT_FOUND, init=False 151 )
Raised when an unknown resource was not found.
2def __init__(self, *, error_code, throttle_seconds, url, body, headers, message, error_status, message_data): 3 self.error_code = error_code 4 self.throttle_seconds = throttle_seconds 5 self.url = url 6 self.body = body 7 self.headers = headers 8 self.message = message 9 self.error_status = error_status 10 self.message_data = message_data 11 self.http_status = attr_dict['http_status'].default 12 BaseException.__init__(self, self.error_code,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)
Method generated by attrs for class NotFound.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
93@typing.final 94class ObjectiveUIStyle(int, enums.Enum): 95 NONE = 0 96 HIGHLIGHTED = 1 97 CRAFTING_WEAPON_LEVEL = 2 98 CRAFTING_WEAPON_LEVEL_PROGRESS = 3 99 CRAFTING_WEAPON_TIMESTAMP = 4 100 CRAFTING_MEMENTOS = 5 101 CRAFTING_MEMENTO_TITLE = 6
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
219@typing.final 220class Place(int, Enum): 221 """An Enum for Destiny 2 Places and NOT Planets""" 222 223 ORBIT = 2961497387 224 SOCIAL = 4151112093 225 LIGHT_HOUSE = 4276116472 226 EXPLORE = 3497767639
An Enum for Destiny 2 Places and NOT Planets
184@typing.final 185class Planet(int, Enum): 186 """An Enum for all available planets in Destiny 2.""" 187 188 UNKNOWN = 0 189 """Unknown space""" 190 191 EARTH = 3747705955 192 """Earth""" 193 194 DREAMING_CITY = 2877881518 195 """The Dreaming city.""" 196 197 NESSUS = 3526908984 198 """Nessus""" 199 200 MOON = 3325508439 201 """The Moon""" 202 203 COSMODROME = 3990611421 204 """The Cosmodrome""" 205 206 TANGLED_SHORE = 3821439926 207 """The Tangled Shore""" 208 209 VENUS = 3871070152 210 """Venus""" 211 212 EAZ = 541863059 # Exclusive event. 213 """European Aerial Zone""" 214 215 EUROPA = 1729879943 216 """Europa"""
An Enum for all available planets in Destiny 2.
672@typing.final 673class Presence(int, Enum): 674 """An enum for a bungie friend status.""" 675 676 OFFLINE_OR_UNKNOWN = 0 677 ONLINE = 1
An enum for a bungie friend status.
760@typing.final 761class PrivacySetting(int, Enum): 762 """An enum for players's privacy settings.""" 763 764 OPEN = 0 765 CLAN_AND_FRIENDS = 1 766 FRIENDS_ONLY = 2 767 INVITE_ONLY = 3 768 CLOSED = 4
An enum for players's privacy settings.
310class RESTClient(interfaces.RESTInterface): 311 """A single process REST client implementation. 312 313 This client is designed to only make HTTP requests and return raw JSON objects. 314 315 Example 316 ------- 317 ```py 318 import aiobungie 319 320 client = aiobungie.RESTClient("TOKEN") 321 async with client: 322 response = await client.fetch_clan_members(4389205) 323 for member in response['results']: 324 print(member['destinyUserInfo']) 325 ``` 326 327 Parameters 328 ---------- 329 token : `str` 330 A valid application token from Bungie's developer portal. 331 332 Other Parameters 333 ---------------- 334 max_retries : `int` 335 The max retries number to retry if the request hit a `5xx` status code. 336 client_secret : `str | None` 337 An optional application client secret, 338 This is only needed if you're fetching OAuth2 tokens with this client. 339 client_id : `int | None` 340 An optional application client id, 341 This is only needed if you're fetching OAuth2 tokens with this client. 342 debug : `bool | str` 343 Whether to enable logging responses or not. 344 345 Logging Levels 346 -------------- 347 * `False`: This will disable logging. 348 * `True`: This will set the level to `DEBUG` and enable logging minimal information. 349 * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information. 350 """ 351 352 __slots__ = ( 353 "_token", 354 "_session", 355 "_lock", 356 "_max_retries", 357 "_client_secret", 358 "_client_id", 359 "_metadata", 360 "_dumps", 361 "_loads", 362 ) 363 364 def __init__( 365 self, 366 token: str, 367 /, 368 *, 369 client_secret: str | None = None, 370 client_id: int | None = None, 371 client_session: aiohttp.ClientSession | None = None, 372 dumps: typedefs.Dumps = helpers.dumps, 373 loads: typedefs.Loads = helpers.loads, 374 max_retries: int = 4, 375 debug: typing.Literal["TRACE"] | bool | int = False, 376 ) -> None: 377 self._session = client_session 378 self._lock: asyncio.Lock | None = None 379 self._client_secret = client_secret 380 self._client_id = client_id 381 self._token: str = token 382 self._max_retries = max_retries 383 self._dumps = dumps 384 self._loads = loads 385 self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {} 386 self.with_debug(debug) 387 388 @property 389 def client_id(self) -> int | None: 390 return self._client_id 391 392 @property 393 def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: 394 return self._metadata 395 396 @property 397 def is_alive(self) -> bool: 398 return self._session is not None 399 400 @typing.final 401 async def close(self) -> None: 402 if self._session is None: 403 raise RuntimeError("REST client is not running.") 404 405 await self._session.close() 406 self._session = None 407 408 @typing.final 409 def open(self) -> None: 410 """Open a new client session. This is called internally with contextmanager usage.""" 411 if self._session: 412 raise RuntimeError("Cannot open REST client when it's already open.") 413 414 self._session = aiohttp.ClientSession( 415 connector=aiohttp.TCPConnector(), 416 connector_owner=True, 417 raise_for_status=False, 418 timeout=aiohttp.ClientTimeout(total=30.0), 419 ) 420 421 @typing.final 422 async def static_request( 423 self, 424 method: _HTTP_METHOD, 425 path: str, 426 *, 427 auth: str | None = None, 428 json: collections.Mapping[str, typing.Any] | None = None, 429 ) -> typedefs.JSONIsh: 430 return await self._request(method, path, auth=auth, json=json) 431 432 @typing.overload 433 def build_oauth2_url(self, client_id: int) -> builders.OAuthURL: ... 434 435 @typing.overload 436 def build_oauth2_url(self) -> builders.OAuthURL | None: ... 437 438 @typing.final 439 def build_oauth2_url( 440 self, client_id: int | None = None 441 ) -> builders.OAuthURL | None: 442 client_id = client_id or self._client_id 443 if client_id is None: 444 return None 445 446 return builders.OAuthURL(client_id=client_id) 447 448 @typing.final 449 def with_debug( 450 self, 451 level: typing.Literal["TRACE"] | bool | int = True, 452 file: str | pathlib.Path | None = None, 453 ) -> None: 454 """Enable debugging for this client with a level. Defaults to `True`. 455 456 Parameters 457 ---------- 458 level: `NotRequired[int | bool | typing.Literal["TRACE"] | None]` 459 The level of the logger. This field is not required. 460 file: `pathlib.Path | str | None` 461 An optional file to write the logs into. 462 463 Logging Levels 464 -------------- 465 * `False`: This will disable logging. 466 * `True`: This will set the level to `DEBUG` and enable logging minimal information. 467 * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information. 468 """ 469 logging.logThreads = False 470 logging.logMultiprocessing = False 471 logging.logProcesses = False 472 logging.captureWarnings(True) 473 474 format = "%(levelname)s " "%(asctime)23.23s " "%(name)s: " "%(message)s" 475 476 file_handler = (logging.FileHandler(file),) if file else None 477 if level == "TRACE" or level == TRACE: 478 logging.basicConfig( 479 level=TRACE, format=format, stream=sys.stdout, handlers=file_handler 480 ) 481 482 elif level is True: 483 logging.basicConfig( 484 level=logging.DEBUG, 485 format=format, 486 stream=sys.stdout, 487 handlers=file_handler, 488 ) 489 490 async def _request( 491 self, 492 method: _HTTP_METHOD, 493 route: str, 494 *, 495 base: bool = False, 496 oauth2: bool = False, 497 auth: str | None = None, 498 unwrap_bytes: bool = False, 499 json: collections.Mapping[str, typing.Any] | None = None, 500 data: collections.Mapping[str, typing.Any] | None = None, 501 params: collections.Mapping[str, typing.Any] | None = None, 502 ) -> typedefs.JSONIsh: 503 # This is not None when opening the client. 504 assert self._session is not None 505 506 retries: int = 0 507 headers: collections.MutableMapping[str, typing.Any] = {} 508 509 headers[_USER_AGENT_HEADERS] = _USER_AGENT 510 headers["X-API-KEY"] = self._token 511 512 if auth is not None: 513 headers[_AUTH_HEADER] = f"Bearer {auth}" 514 515 # Handling endpoints 516 endpoint = url.BASE 517 518 if not base: 519 endpoint = endpoint + url.REST_EP 520 521 if oauth2: 522 assert self._client_id 523 assert self._client_secret 524 headers["client_secret"] = self._client_secret 525 526 headers["Content-Type"] = "application/x-www-form-urlencoded" 527 endpoint = endpoint + url.TOKEN_EP 528 529 if self._lock is None: 530 self._lock = asyncio.Lock() 531 532 if json: 533 headers["Content-Type"] = _APP_JSON 534 535 stack = contextlib.AsyncExitStack() 536 while True: 537 try: 538 await stack.enter_async_context(self._lock) 539 540 # We make the request here. 541 taken_time = time.monotonic() 542 response = await self._session.request( 543 method=method, 544 url=f"{endpoint}/{route}", 545 headers=headers, 546 data=_JSONPayload(json) if json else data, 547 params=params, 548 ) 549 response_time = (time.monotonic() - taken_time) * 1_000 550 551 _LOGGER.debug( 552 "%s %s %s Time %.4fms", 553 method, 554 f"{endpoint}/{route}", 555 f"{response.status} {response.reason}", 556 response_time, 557 ) 558 559 await self._handle_ratelimit(response, method, route) 560 561 except aiohttp.ClientConnectionError as exc: 562 if retries >= self._max_retries: 563 raise error.HTTPError( 564 str(exc), 565 http.HTTPStatus.SERVICE_UNAVAILABLE, 566 ) 567 backoff_ = backoff.ExponentialBackOff(maximum=8) 568 569 timer = next(backoff_) 570 _LOGGER.warning( 571 "Client received a connection error <%s> Retrying in %.2fs. Remaining retries: %s", 572 type(exc).__qualname__, 573 timer, 574 self._max_retries - retries, 575 ) 576 retries += 1 577 await asyncio.sleep(timer) 578 continue 579 580 finally: 581 await stack.aclose() 582 583 if response.status == http.HTTPStatus.NO_CONTENT: 584 return None 585 586 # Handle the successful response. 587 if 300 > response.status >= 200: 588 if unwrap_bytes: 589 # We need to read the bytes for the manifest response. 590 return await response.read() 591 592 # Bungie get funky and return HTML instead of JSON when making an authorized 593 # request with a dummy access token. We could technically read the page content 594 # but that's Bungie's fault for not returning a JSON response. 595 if response.content_type != _APP_JSON: 596 raise error.HTTPError( 597 message=f"Expected JSON response, Got {response.content_type}, " 598 f"{response.real_url.human_repr()}", 599 http_status=http.HTTPStatus(response.status), 600 ) 601 602 json_data = self._loads(await response.read()) 603 604 _LOGGER.debug( 605 "%s %s %s Time %.4fms", 606 method, 607 f"{endpoint}/{route}", 608 f"{response.status} {response.reason}", 609 response_time, 610 ) 611 612 if _LOGGER.isEnabledFor(TRACE): 613 _LOGGER.log( 614 TRACE, 615 "%s", 616 error.stringify_headers(dict(response.headers)), 617 ) 618 619 details: collections.MutableMapping[str, typing.Any] = {} 620 if json: 621 details["json"] = json 622 623 if data: 624 details["data"] = data 625 626 if params: 627 details["params"] = params 628 629 if details: 630 _LOGGER.log(TRACE, "%s", error.stringify_headers(details)) 631 632 # Return the response. 633 # auth responses are not inside a Response object. 634 if oauth2: 635 return json_data 636 637 # The reason we have a type ignore is because the actual response type 638 # is within this `Response` key. 639 return json_data["Response"] # type: ignore 640 641 if ( 642 response.status in _RETRY_5XX and retries < self._max_retries # noqa: W503 643 ): 644 backoff_ = backoff.ExponentialBackOff(maximum=6) 645 sleep_time = next(backoff_) 646 _LOGGER.warning( 647 "Got %i - %s. Sleeping for %.2f seconds. Remaining retries: %i", 648 response.status, 649 response.reason, 650 sleep_time, 651 self._max_retries - retries, 652 ) 653 654 retries += 1 655 await asyncio.sleep(sleep_time) 656 continue 657 658 raise await error.panic(response) 659 660 async def __aenter__(self) -> RESTClient: 661 self.open() 662 return self 663 664 async def __aexit__( 665 self, 666 exception_type: type[BaseException] | None, 667 exception: BaseException | None, 668 exception_traceback: types.TracebackType | None, 669 ) -> None: 670 await self.close() 671 672 # We don't want this to be super complicated. 673 @typing.final 674 async def _handle_ratelimit( 675 self, 676 response: aiohttp.ClientResponse, 677 method: str, 678 route: str, 679 ) -> None: 680 if response.status != http.HTTPStatus.TOO_MANY_REQUESTS: 681 return 682 683 if response.content_type != _APP_JSON: 684 raise error.HTTPError( 685 f"Being ratelimited on non JSON request, {response.content_type}.", 686 http.HTTPStatus.TOO_MANY_REQUESTS, 687 ) 688 689 # The reason we have a type ignore here is that we guaranteed the content type is JSON above. 690 json: typedefs.JSONObject = self._loads(await response.read()) # type: ignore 691 retry_after = float(json.get("ThrottleSeconds", 15.0)) + 0.1 692 max_calls: int = 0 693 694 while True: 695 if max_calls == 10: 696 # Max retries by default. We raise an error here. 697 raise error.RateLimitedError( 698 body=json, 699 url=str(response.real_url), 700 retry_after=retry_after, 701 ) 702 703 # We sleep for a little bit to avoid funky behavior. 704 _LOGGER.warning( 705 "We're being ratelimited, Method %s Route %s. Sleeping for %.2fs.", 706 method, 707 route, 708 retry_after, 709 ) 710 await asyncio.sleep(retry_after) 711 max_calls += 1 712 continue 713 714 async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response: 715 data = { 716 "grant_type": "authorization_code", 717 "code": code, 718 "client_id": self._client_id, 719 "client_secret": self._client_secret, 720 } 721 722 response = await self._request(_POST, "", data=data, oauth2=True) 723 assert isinstance(response, dict) 724 return builders.OAuth2Response.build_response(response) 725 726 async def refresh_access_token( 727 self, refresh_token: str, / 728 ) -> builders.OAuth2Response: 729 data = { 730 "grant_type": "refresh_token", 731 "refresh_token": refresh_token, 732 "client_id": self._client_id, 733 "client_secret": self._client_secret, 734 } 735 736 response = await self._request(_POST, "", data=data, oauth2=True) 737 assert isinstance(response, dict) 738 return builders.OAuth2Response.build_response(response) 739 740 async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject: 741 resp = await self._request(_GET, f"User/GetBungieNetUserById/{id}/") 742 assert isinstance(resp, dict) 743 return resp 744 745 async def fetch_user_themes(self) -> typedefs.JSONArray: 746 resp = await self._request(_GET, "User/GetAvailableThemes/") 747 assert isinstance(resp, list) 748 return resp 749 750 async def fetch_membership_from_id( 751 self, 752 id: int, 753 type: enums.MembershipType | int = enums.MembershipType.NONE, 754 /, 755 ) -> typedefs.JSONObject: 756 resp = await self._request(_GET, f"User/GetMembershipsById/{id}/{int(type)}") 757 assert isinstance(resp, dict) 758 return resp 759 760 async def fetch_membership( 761 self, 762 name: str, 763 code: int, 764 type: enums.MembershipType | int = enums.MembershipType.ALL, 765 /, 766 ) -> typedefs.JSONArray: 767 resp = await self._request( 768 _POST, 769 f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}", 770 json={"displayName": name, "displayNameCode": code}, 771 ) 772 assert isinstance(resp, list) 773 return resp 774 775 async def search_users(self, name: str, /) -> typedefs.JSONObject: 776 resp = await self._request( 777 _POST, 778 "User/Search/GlobalName/0", 779 json={"displayNamePrefix": name}, 780 ) 781 assert isinstance(resp, dict) 782 return resp 783 784 async def fetch_clan_from_id( 785 self, id: int, /, access_token: str | None = None 786 ) -> typedefs.JSONObject: 787 resp = await self._request(_GET, f"GroupV2/{id}", auth=access_token) 788 assert isinstance(resp, dict) 789 return resp 790 791 async def fetch_clan( 792 self, 793 name: str, 794 /, 795 access_token: str | None = None, 796 *, 797 type: enums.GroupType | int = enums.GroupType.CLAN, 798 ) -> typedefs.JSONObject: 799 resp = await self._request( 800 _GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token 801 ) 802 assert isinstance(resp, dict) 803 return resp 804 805 async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject: 806 resp = await self._request(_GET, f"GroupV2/{clan_id}/AdminsAndFounder/") 807 assert isinstance(resp, dict) 808 return resp 809 810 async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray: 811 resp = await self._request(_GET, f"GroupV2/{clan_id}/OptionalConversations/") 812 assert isinstance(resp, list) 813 return resp 814 815 async def fetch_application(self, appid: int, /) -> typedefs.JSONObject: 816 resp = await self._request(_GET, f"App/Application/{appid}") 817 assert isinstance(resp, dict) 818 return resp 819 820 async def fetch_character( 821 self, 822 member_id: int, 823 membership_type: enums.MembershipType | int, 824 character_id: int, 825 components: collections.Sequence[enums.ComponentType], 826 auth: str | None = None, 827 ) -> typedefs.JSONObject: 828 collector = _collect_components(components) 829 response = await self._request( 830 _GET, 831 f"Destiny2/{int(membership_type)}/Profile/{member_id}/" 832 f"Character/{character_id}/?components={collector}", 833 auth=auth, 834 ) 835 assert isinstance(response, dict) 836 return response 837 838 async def fetch_activities( 839 self, 840 member_id: int, 841 character_id: int, 842 mode: enums.GameMode | int, 843 membership_type: enums.MembershipType | int = enums.MembershipType.ALL, 844 *, 845 page: int = 0, 846 limit: int = 1, 847 ) -> typedefs.JSONObject: 848 resp = await self._request( 849 _GET, 850 f"Destiny2/{int(membership_type)}/Account/" 851 f"{member_id}/Character/{character_id}/Stats/Activities" 852 f"/?mode={int(mode)}&count={limit}&page={page}", 853 ) 854 assert isinstance(resp, dict) 855 return resp 856 857 async def fetch_vendor_sales(self) -> typedefs.JSONObject: 858 resp = await self._request( 859 _GET, 860 f"Destiny2/Vendors/?components={int(enums.ComponentType.VENDOR_SALES)}", 861 ) 862 assert isinstance(resp, dict) 863 return resp 864 865 async def fetch_profile( 866 self, 867 membership_id: int, 868 type: enums.MembershipType | int, 869 components: collections.Sequence[enums.ComponentType], 870 auth: str | None = None, 871 ) -> typedefs.JSONObject: 872 collector = _collect_components(components) 873 response = await self._request( 874 _GET, 875 f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}", 876 auth=auth, 877 ) 878 assert isinstance(response, dict) 879 return response 880 881 async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject: 882 response = await self._request(_GET, route=f"Destiny2/Manifest/{type}/{hash}") 883 assert isinstance(response, dict) 884 return response 885 886 async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject: 887 resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash) 888 assert isinstance(resp, dict) 889 return resp 890 891 async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject: 892 resp = await self.fetch_entity("DestinyObjectiveDefinition", hash) 893 assert isinstance(resp, dict) 894 return resp 895 896 async def fetch_groups_for_member( 897 self, 898 member_id: int, 899 member_type: enums.MembershipType | int, 900 /, 901 *, 902 filter: int = 0, 903 group_type: enums.GroupType | int = enums.GroupType.CLAN, 904 ) -> typedefs.JSONObject: 905 resp = await self._request( 906 _GET, 907 f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/", 908 ) 909 assert isinstance(resp, dict) 910 return resp 911 912 async def fetch_potential_groups_for_member( 913 self, 914 member_id: int, 915 member_type: enums.MembershipType | int, 916 /, 917 *, 918 filter: int = 0, 919 group_type: enums.GroupType | int = enums.GroupType.CLAN, 920 ) -> typedefs.JSONObject: 921 resp = await self._request( 922 _GET, 923 f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/", 924 ) 925 assert isinstance(resp, dict) 926 return resp 927 928 async def fetch_clan_members( 929 self, 930 clan_id: int, 931 /, 932 *, 933 name: str | None = None, 934 type: enums.MembershipType | int = enums.MembershipType.NONE, 935 ) -> typedefs.JSONObject: 936 resp = await self._request( 937 _GET, 938 f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}¤tpage=1", 939 ) 940 assert isinstance(resp, dict) 941 return resp 942 943 async def fetch_hardlinked_credentials( 944 self, 945 credential: int, 946 type: enums.CredentialType | int = enums.CredentialType.STEAMID, 947 /, 948 ) -> typedefs.JSONObject: 949 resp = await self._request( 950 _GET, 951 f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/", 952 ) 953 assert isinstance(resp, dict) 954 return resp 955 956 async def fetch_user_credentials( 957 self, access_token: str, membership_id: int, / 958 ) -> typedefs.JSONArray: 959 resp = await self._request( 960 _GET, 961 f"User/GetCredentialTypesForTargetAccount/{membership_id}", 962 auth=access_token, 963 ) 964 assert isinstance(resp, list) 965 return resp 966 967 async def insert_socket_plug( 968 self, 969 action_token: str, 970 /, 971 instance_id: int, 972 plug: builders.PlugSocketBuilder | collections.Mapping[str, int], 973 character_id: int, 974 membership_type: enums.MembershipType | int, 975 ) -> typedefs.JSONObject: 976 if isinstance(plug, builders.PlugSocketBuilder): 977 plug = plug.collect() 978 979 body = { 980 "actionToken": action_token, 981 "itemInstanceId": instance_id, 982 "plug": plug, 983 "characterId": character_id, 984 "membershipType": int(membership_type), 985 } 986 resp = await self._request( 987 _POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body 988 ) 989 assert isinstance(resp, dict) 990 return resp 991 992 async def insert_socket_plug_free( 993 self, 994 access_token: str, 995 /, 996 instance_id: int, 997 plug: builders.PlugSocketBuilder | collections.Mapping[str, int], 998 character_id: int, 999 membership_type: enums.MembershipType | int, 1000 ) -> typedefs.JSONObject: 1001 if isinstance(plug, builders.PlugSocketBuilder): 1002 plug = plug.collect() 1003 1004 body = { 1005 "itemInstanceId": instance_id, 1006 "plug": plug, 1007 "characterId": character_id, 1008 "membershipType": int(membership_type), 1009 } 1010 resp = await self._request( 1011 _POST, 1012 "Destiny2/Actions/Items/InsertSocketPlugFree", 1013 json=body, 1014 auth=access_token, 1015 ) 1016 assert isinstance(resp, dict) 1017 return resp 1018 1019 @helpers.unstable 1020 async def set_item_lock_state( 1021 self, 1022 access_token: str, 1023 state: bool, 1024 /, 1025 item_id: int, 1026 character_id: int, 1027 membership_type: enums.MembershipType | int, 1028 ) -> int: 1029 body = { 1030 "state": state, 1031 "itemId": item_id, 1032 "characterId": character_id, 1033 "membershipType": int(membership_type), 1034 } 1035 response = await self._request( 1036 _POST, 1037 "Destiny2/Actions/Items/SetLockState", 1038 json=body, 1039 auth=access_token, 1040 ) 1041 assert isinstance(response, int) 1042 return response 1043 1044 async def set_quest_track_state( 1045 self, 1046 access_token: str, 1047 state: bool, 1048 /, 1049 item_id: int, 1050 character_id: int, 1051 membership_type: enums.MembershipType | int, 1052 ) -> int: 1053 body = { 1054 "state": state, 1055 "itemId": item_id, 1056 "characterId": character_id, 1057 "membership_type": int(membership_type), 1058 } 1059 response = await self._request( 1060 _POST, 1061 "Destiny2/Actions/Items/SetTrackedState", 1062 json=body, 1063 auth=access_token, 1064 ) 1065 assert isinstance(response, int) 1066 return response 1067 1068 async def fetch_manifest_path(self) -> typedefs.JSONObject: 1069 path = await self._request(_GET, "Destiny2/Manifest") 1070 assert isinstance(path, dict) 1071 return path 1072 1073 async def read_manifest_bytes(self, language: str = "en", /) -> bytes: 1074 _ensure_manifest_language(language) 1075 1076 content = await self.fetch_manifest_path() 1077 resp = await self._request( 1078 _GET, 1079 content["mobileWorldContentPaths"][language], 1080 unwrap_bytes=True, 1081 base=True, 1082 ) 1083 assert isinstance(resp, bytes) 1084 return resp 1085 1086 async def download_sqlite_manifest( 1087 self, 1088 language: str = "en", 1089 name: str = "manifest", 1090 path: pathlib.Path | str = ".", 1091 *, 1092 force: bool = False, 1093 executor: concurrent.futures.Executor | None = None, 1094 ) -> pathlib.Path: 1095 complete_path = _get_path(name, path, sql=True) 1096 1097 if complete_path.exists() and force: 1098 if force: 1099 _LOGGER.info( 1100 f"Found manifest in {complete_path!s}. Forcing to Re-Download." 1101 ) 1102 complete_path.unlink(missing_ok=True) 1103 1104 return await self.download_sqlite_manifest( 1105 language, name, path, force=force 1106 ) 1107 1108 else: 1109 raise FileExistsError( 1110 "Manifest file already exists, " 1111 "To force download, set the `force` parameter to `True`." 1112 ) 1113 1114 _LOGGER.info(f"Downloading manifest. Location: {complete_path!s}") 1115 data_bytes = await self.read_manifest_bytes(language) 1116 await asyncio.get_running_loop().run_in_executor( 1117 executor, _write_sqlite_bytes, data_bytes, path, name 1118 ) 1119 _LOGGER.info("Finished downloading manifest.") 1120 return _get_path(name, path, sql=True) 1121 1122 async def download_json_manifest( 1123 self, 1124 file_name: str = "manifest", 1125 path: str | pathlib.Path = ".", 1126 *, 1127 language: str = "en", 1128 executor: concurrent.futures.Executor | None = None, 1129 ) -> pathlib.Path: 1130 _ensure_manifest_language(language) 1131 full_path = _get_path(file_name, path) 1132 _LOGGER.info(f"Downloading manifest JSON to {full_path!r}...") 1133 1134 content = await self.fetch_manifest_path() 1135 json_bytes = await self._request( 1136 _GET, 1137 content["jsonWorldContentPaths"][language], 1138 unwrap_bytes=True, 1139 base=True, 1140 ) 1141 1142 assert isinstance(json_bytes, bytes) 1143 await asyncio.get_running_loop().run_in_executor( 1144 executor, _write_json_bytes, json_bytes, file_name, path 1145 ) 1146 _LOGGER.info("Finished downloading manifest JSON.") 1147 return full_path 1148 1149 async def fetch_manifest_version(self) -> str: 1150 # This is guaranteed str. 1151 return (await self.fetch_manifest_path())["version"] 1152 1153 async def fetch_linked_profiles( 1154 self, 1155 member_id: int, 1156 member_type: enums.MembershipType | int, 1157 /, 1158 *, 1159 all: bool = False, 1160 ) -> typedefs.JSONObject: 1161 resp = await self._request( 1162 _GET, 1163 f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}", 1164 ) 1165 assert isinstance(resp, dict) 1166 return resp 1167 1168 async def fetch_clan_banners(self) -> typedefs.JSONObject: 1169 resp = await self._request(_GET, "Destiny2/Clan/ClanBannerDictionary/") 1170 assert isinstance(resp, dict) 1171 return resp 1172 1173 async def fetch_public_milestones(self) -> typedefs.JSONObject: 1174 resp = await self._request(_GET, "Destiny2/Milestones/") 1175 assert isinstance(resp, dict) 1176 return resp 1177 1178 async def fetch_public_milestone_content( 1179 self, milestone_hash: int, / 1180 ) -> typedefs.JSONObject: 1181 resp = await self._request( 1182 _GET, f"Destiny2/Milestones/{milestone_hash}/Content/" 1183 ) 1184 assert isinstance(resp, dict) 1185 return resp 1186 1187 async def fetch_current_user_memberships( 1188 self, access_token: str, / 1189 ) -> typedefs.JSONObject: 1190 resp = await self._request( 1191 _GET, 1192 "User/GetMembershipsForCurrentUser/", 1193 auth=access_token, 1194 ) 1195 assert isinstance(resp, dict) 1196 return resp 1197 1198 async def equip_item( 1199 self, 1200 access_token: str, 1201 /, 1202 item_id: int, 1203 character_id: int, 1204 membership_type: enums.MembershipType | int, 1205 ) -> None: 1206 payload = { 1207 "itemId": item_id, 1208 "characterId": character_id, 1209 "membershipType": int(membership_type), 1210 } 1211 1212 await self._request( 1213 _POST, 1214 "Destiny2/Actions/Items/EquipItem/", 1215 json=payload, 1216 auth=access_token, 1217 ) 1218 1219 async def equip_items( 1220 self, 1221 access_token: str, 1222 /, 1223 item_ids: collections.Sequence[int], 1224 character_id: int, 1225 membership_type: enums.MembershipType | int, 1226 ) -> None: 1227 payload = { 1228 "itemIds": item_ids, 1229 "characterId": character_id, 1230 "membershipType": int(membership_type), 1231 } 1232 await self._request( 1233 _POST, 1234 "Destiny2/Actions/Items/EquipItems/", 1235 json=payload, 1236 auth=access_token, 1237 ) 1238 1239 async def ban_clan_member( 1240 self, 1241 access_token: str, 1242 /, 1243 group_id: int, 1244 membership_id: int, 1245 membership_type: enums.MembershipType | int, 1246 *, 1247 length: int = 0, 1248 comment: str | None = None, 1249 ) -> None: 1250 payload = {"comment": str(comment), "length": length} 1251 await self._request( 1252 _POST, 1253 f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/", 1254 json=payload, 1255 auth=access_token, 1256 ) 1257 1258 async def unban_clan_member( 1259 self, 1260 access_token: str, 1261 /, 1262 group_id: int, 1263 membership_id: int, 1264 membership_type: enums.MembershipType | int, 1265 ) -> None: 1266 await self._request( 1267 _POST, 1268 f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/", 1269 auth=access_token, 1270 ) 1271 1272 async def kick_clan_member( 1273 self, 1274 access_token: str, 1275 /, 1276 group_id: int, 1277 membership_id: int, 1278 membership_type: enums.MembershipType | int, 1279 ) -> typedefs.JSONObject: 1280 resp = await self._request( 1281 _POST, 1282 f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/", 1283 auth=access_token, 1284 ) 1285 assert isinstance(resp, dict) 1286 return resp 1287 1288 async def edit_clan( 1289 self, 1290 access_token: str, 1291 /, 1292 group_id: int, 1293 *, 1294 name: str | None = None, 1295 about: str | None = None, 1296 motto: str | None = None, 1297 theme: str | None = None, 1298 tags: collections.Sequence[str] | None = None, 1299 is_public: bool | None = None, 1300 locale: str | None = None, 1301 avatar_image_index: int | None = None, 1302 membership_option: enums.MembershipOption | int | None = None, 1303 allow_chat: bool | None = None, 1304 chat_security: typing.Literal[0, 1] | None = None, 1305 call_sign: str | None = None, 1306 homepage: typing.Literal[0, 1, 2] | None = None, 1307 enable_invite_messaging_for_admins: bool | None = None, 1308 default_publicity: typing.Literal[0, 1, 2] | None = None, 1309 is_public_topic_admin: bool | None = None, 1310 ) -> None: 1311 payload = { 1312 "name": name, 1313 "about": about, 1314 "motto": motto, 1315 "theme": theme, 1316 "tags": tags, 1317 "isPublic": is_public, 1318 "avatarImageIndex": avatar_image_index, 1319 "isPublicTopicAdminOnly": is_public_topic_admin, 1320 "allowChat": allow_chat, 1321 "chatSecurity": chat_security, 1322 "callsign": call_sign, 1323 "homepage": homepage, 1324 "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins, 1325 "defaultPublicity": default_publicity, 1326 "locale": locale, 1327 } 1328 if membership_option is not None: 1329 payload["membershipOption"] = int(membership_option) 1330 1331 await self._request( 1332 _POST, 1333 f"GroupV2/{group_id}/Edit", 1334 json=payload, 1335 auth=access_token, 1336 ) 1337 1338 async def edit_clan_options( 1339 self, 1340 access_token: str, 1341 /, 1342 group_id: int, 1343 *, 1344 invite_permissions_override: bool | None = None, 1345 update_culture_permissionOverride: bool | None = None, 1346 host_guided_game_permission_override: typing.Literal[0, 1, 2] | None = None, 1347 update_banner_permission_override: bool | None = None, 1348 join_level: enums.ClanMemberType | int | None = None, 1349 ) -> None: 1350 payload = { 1351 "InvitePermissionOverride": invite_permissions_override, 1352 "UpdateCulturePermissionOverride": update_culture_permissionOverride, 1353 "HostGuidedGamePermissionOverride": host_guided_game_permission_override, 1354 "UpdateBannerPermissionOverride": update_banner_permission_override, 1355 "JoinLevel": int(join_level) if join_level else None, 1356 } 1357 1358 await self._request( 1359 _POST, 1360 f"GroupV2/{group_id}/EditFounderOptions", 1361 json=payload, 1362 auth=access_token, 1363 ) 1364 1365 async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject: 1366 resp = await self._request( 1367 _GET, 1368 "Social/Friends/", 1369 auth=access_token, 1370 ) 1371 assert isinstance(resp, dict) 1372 return resp 1373 1374 async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject: 1375 resp = await self._request( 1376 _GET, 1377 "Social/Friends/Requests", 1378 auth=access_token, 1379 ) 1380 assert isinstance(resp, dict) 1381 return resp 1382 1383 async def accept_friend_request(self, access_token: str, /, member_id: int) -> None: 1384 await self._request( 1385 _POST, 1386 f"Social/Friends/Requests/Accept/{member_id}", 1387 auth=access_token, 1388 ) 1389 1390 async def send_friend_request(self, access_token: str, /, member_id: int) -> None: 1391 await self._request( 1392 _POST, 1393 f"Social/Friends/Add/{member_id}", 1394 auth=access_token, 1395 ) 1396 1397 async def decline_friend_request( 1398 self, access_token: str, /, member_id: int 1399 ) -> None: 1400 await self._request( 1401 _POST, 1402 f"Social/Friends/Requests/Decline/{member_id}", 1403 auth=access_token, 1404 ) 1405 1406 async def remove_friend(self, access_token: str, /, member_id: int) -> None: 1407 await self._request( 1408 _POST, 1409 f"Social/Friends/Remove/{member_id}", 1410 auth=access_token, 1411 ) 1412 1413 async def remove_friend_request(self, access_token: str, /, member_id: int) -> None: 1414 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1415 await self._request( 1416 _POST, 1417 f"Social/Friends/Requests/Remove/{member_id}", 1418 auth=access_token, 1419 ) 1420 1421 async def approve_all_pending_group_users( 1422 self, 1423 access_token: str, 1424 /, 1425 group_id: int, 1426 message: str | None = None, 1427 ) -> None: 1428 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1429 await self._request( 1430 _POST, 1431 f"GroupV2/{group_id}/Members/ApproveAll", 1432 auth=access_token, 1433 json={"message": str(message)}, 1434 ) 1435 1436 async def deny_all_pending_group_users( 1437 self, 1438 access_token: str, 1439 /, 1440 group_id: int, 1441 *, 1442 message: str | None = None, 1443 ) -> None: 1444 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1445 await self._request( 1446 _POST, 1447 f"GroupV2/{group_id}/Members/DenyAll", 1448 auth=access_token, 1449 json={"message": str(message)}, 1450 ) 1451 1452 async def add_optional_conversation( 1453 self, 1454 access_token: str, 1455 /, 1456 group_id: int, 1457 *, 1458 name: str | None = None, 1459 security: typing.Literal[0, 1] = 0, 1460 ) -> None: 1461 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1462 payload = {"chatName": str(name), "chatSecurity": security} 1463 await self._request( 1464 _POST, 1465 f"GroupV2/{group_id}/OptionalConversations/Add", 1466 json=payload, 1467 auth=access_token, 1468 ) 1469 1470 async def edit_optional_conversation( 1471 self, 1472 access_token: str, 1473 /, 1474 group_id: int, 1475 conversation_id: int, 1476 *, 1477 name: str | None = None, 1478 security: typing.Literal[0, 1] = 0, 1479 enable_chat: bool = False, 1480 ) -> None: 1481 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1482 payload = { 1483 "chatEnabled": enable_chat, 1484 "chatName": str(name), 1485 "chatSecurity": security, 1486 } 1487 await self._request( 1488 _POST, 1489 f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}", 1490 json=payload, 1491 auth=access_token, 1492 ) 1493 1494 async def transfer_item( 1495 self, 1496 access_token: str, 1497 /, 1498 item_id: int, 1499 item_hash: int, 1500 character_id: int, 1501 member_type: enums.MembershipType | int, 1502 *, 1503 stack_size: int = 1, 1504 vault: bool = False, 1505 ) -> None: 1506 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1507 payload = { 1508 "characterId": character_id, 1509 "membershipType": int(member_type), 1510 "itemId": item_id, 1511 "itemReferenceHash": item_hash, 1512 "stackSize": stack_size, 1513 "transferToVault": vault, 1514 } 1515 await self._request( 1516 _POST, 1517 "Destiny2/Actions/Items/TransferItem", 1518 json=payload, 1519 auth=access_token, 1520 ) 1521 1522 async def pull_item( 1523 self, 1524 access_token: str, 1525 /, 1526 item_id: int, 1527 item_hash: int, 1528 character_id: int, 1529 member_type: enums.MembershipType | int, 1530 *, 1531 stack_size: int = 1, 1532 vault: bool = False, 1533 ) -> None: 1534 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1535 payload = { 1536 "characterId": character_id, 1537 "membershipType": int(member_type), 1538 "itemId": item_id, 1539 "itemReferenceHash": item_hash, 1540 "stackSize": stack_size, 1541 "transferToVault": vault, 1542 } 1543 await self._request( 1544 _POST, 1545 "Destiny2/Actions/Items/PullFromPostmaster", 1546 json=payload, 1547 auth=access_token, 1548 ) 1549 1550 async def fetch_fireteams( 1551 self, 1552 activity_type: fireteams.FireteamActivity | int, 1553 *, 1554 platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY, 1555 language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL, 1556 date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL, 1557 page: int = 0, 1558 slots_filter: int = 0, 1559 ) -> typedefs.JSONObject: 1560 resp = await self._request( 1561 _GET, 1562 f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}", # noqa: E501 Line too long 1563 ) 1564 assert isinstance(resp, dict) 1565 return resp 1566 1567 async def fetch_available_clan_fireteams( 1568 self, 1569 access_token: str, 1570 group_id: int, 1571 activity_type: fireteams.FireteamActivity | int, 1572 *, 1573 platform: fireteams.FireteamPlatform | int, 1574 language: fireteams.FireteamLanguage | str, 1575 date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL, 1576 page: int = 0, 1577 public_only: bool = False, 1578 slots_filter: int = 0, 1579 ) -> typedefs.JSONObject: 1580 resp = await self._request( 1581 _GET, 1582 f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}", # noqa: E501 1583 json={"langFilter": str(language)}, 1584 auth=access_token, 1585 ) 1586 assert isinstance(resp, dict) 1587 return resp 1588 1589 async def fetch_clan_fireteam( 1590 self, access_token: str, fireteam_id: int, group_id: int 1591 ) -> typedefs.JSONObject: 1592 resp = await self._request( 1593 _GET, 1594 f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}", 1595 auth=access_token, 1596 ) 1597 assert isinstance(resp, dict) 1598 return resp 1599 1600 async def fetch_my_clan_fireteams( 1601 self, 1602 access_token: str, 1603 group_id: int, 1604 *, 1605 include_closed: bool = True, 1606 platform: fireteams.FireteamPlatform | int, 1607 language: fireteams.FireteamLanguage | str, 1608 filtered: bool = True, 1609 page: int = 0, 1610 ) -> typedefs.JSONObject: 1611 payload = {"groupFilter": filtered, "langFilter": str(language)} 1612 1613 resp = await self._request( 1614 _GET, 1615 f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}", 1616 json=payload, 1617 auth=access_token, 1618 ) 1619 assert isinstance(resp, dict) 1620 return resp 1621 1622 async def fetch_private_clan_fireteams( 1623 self, access_token: str, group_id: int, / 1624 ) -> int: 1625 resp = await self._request( 1626 _GET, 1627 f"Fireteam/Clan/{group_id}/ActiveCount", 1628 auth=access_token, 1629 ) 1630 assert isinstance(resp, int) 1631 return resp 1632 1633 async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject: 1634 resp = await self._request( 1635 _GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}" 1636 ) 1637 assert isinstance(resp, dict) 1638 return resp 1639 1640 async def search_entities( 1641 self, name: str, entity_type: str, *, page: int = 0 1642 ) -> typedefs.JSONObject: 1643 resp = await self._request( 1644 _GET, 1645 f"Destiny2/Armory/Search/{entity_type}/{name}/", 1646 json={"page": page}, 1647 ) 1648 assert isinstance(resp, dict) 1649 return resp 1650 1651 async def fetch_unique_weapon_history( 1652 self, 1653 membership_id: int, 1654 character_id: int, 1655 membership_type: enums.MembershipType | int, 1656 ) -> typedefs.JSONObject: 1657 resp = await self._request( 1658 _GET, 1659 f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/", 1660 ) 1661 assert isinstance(resp, dict) 1662 return resp 1663 1664 async def fetch_item( 1665 self, 1666 member_id: int, 1667 item_id: int, 1668 membership_type: enums.MembershipType | int, 1669 components: collections.Sequence[enums.ComponentType], 1670 ) -> typedefs.JSONObject: 1671 collector = _collect_components(components) 1672 1673 resp = await self._request( 1674 _GET, 1675 f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}", 1676 ) 1677 assert isinstance(resp, dict) 1678 return resp 1679 1680 async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject: 1681 resp = await self._request(_GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/") 1682 assert isinstance(resp, dict) 1683 return resp 1684 1685 async def fetch_available_locales(self) -> typedefs.JSONObject: 1686 resp = await self._request(_GET, "Destiny2/Manifest/DestinyLocaleDefinition/") 1687 assert isinstance(resp, dict) 1688 return resp 1689 1690 async def fetch_common_settings(self) -> typedefs.JSONObject: 1691 resp = await self._request(_GET, "Settings") 1692 assert isinstance(resp, dict) 1693 return resp 1694 1695 async def fetch_user_systems_overrides(self) -> typedefs.JSONObject: 1696 resp = await self._request(_GET, "UserSystemOverrides") 1697 assert isinstance(resp, dict) 1698 return resp 1699 1700 async def fetch_global_alerts( 1701 self, *, include_streaming: bool = False 1702 ) -> typedefs.JSONArray: 1703 resp = await self._request( 1704 _GET, f"GlobalAlerts/?includestreaming={include_streaming}" 1705 ) 1706 assert isinstance(resp, list) 1707 return resp 1708 1709 async def awainitialize_request( 1710 self, 1711 access_token: str, 1712 type: typing.Literal[0, 1], 1713 membership_type: enums.MembershipType | int, 1714 /, 1715 *, 1716 affected_item_id: int | None = None, 1717 character_id: int | None = None, 1718 ) -> typedefs.JSONObject: 1719 body = {"type": type, "membershipType": int(membership_type)} 1720 1721 if affected_item_id is not None: 1722 body["affectedItemId"] = affected_item_id 1723 1724 if character_id is not None: 1725 body["characterId"] = character_id 1726 1727 resp = await self._request( 1728 _POST, "Destiny2/Awa/Initialize", json=body, auth=access_token 1729 ) 1730 assert isinstance(resp, dict) 1731 return resp 1732 1733 async def awaget_action_token( 1734 self, access_token: str, correlation_id: str, / 1735 ) -> typedefs.JSONObject: 1736 resp = await self._request( 1737 _POST, 1738 f"Destiny2/Awa/GetActionToken/{correlation_id}", 1739 auth=access_token, 1740 ) 1741 assert isinstance(resp, dict) 1742 return resp 1743 1744 async def awa_provide_authorization_result( 1745 self, 1746 access_token: str, 1747 selection: int, 1748 correlation_id: str, 1749 nonce: collections.MutableSequence[str | bytes], 1750 ) -> int: 1751 body = {"selection": selection, "correlationId": correlation_id, "nonce": nonce} 1752 1753 resp = await self._request( 1754 _POST, 1755 "Destiny2/Awa/AwaProvideAuthorizationResult", 1756 json=body, 1757 auth=access_token, 1758 ) 1759 assert isinstance(resp, int) 1760 return resp 1761 1762 async def fetch_vendors( 1763 self, 1764 access_token: str, 1765 character_id: int, 1766 membership_id: int, 1767 membership_type: enums.MembershipType | int, 1768 /, 1769 components: collections.Sequence[enums.ComponentType], 1770 filter: int | None = None, 1771 ) -> typedefs.JSONObject: 1772 components_ = _collect_components(components) 1773 route = ( 1774 f"Destiny2/{int(membership_type)}/Profile/{membership_id}" 1775 f"/Character/{character_id}/Vendors/?components={components_}" 1776 ) 1777 1778 if filter is not None: 1779 route = route + f"&filter={filter}" 1780 1781 resp = await self._request( 1782 _GET, 1783 route, 1784 auth=access_token, 1785 ) 1786 assert isinstance(resp, dict) 1787 return resp 1788 1789 async def fetch_vendor( 1790 self, 1791 access_token: str, 1792 character_id: int, 1793 membership_id: int, 1794 membership_type: enums.MembershipType | int, 1795 vendor_hash: int, 1796 /, 1797 components: collections.Sequence[enums.ComponentType], 1798 ) -> typedefs.JSONObject: 1799 components_ = _collect_components(components) 1800 resp = await self._request( 1801 _GET, 1802 ( 1803 f"Destiny2/{int(membership_type)}/Profile/{membership_id}" 1804 f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}" 1805 ), 1806 auth=access_token, 1807 ) 1808 assert isinstance(resp, dict) 1809 return resp 1810 1811 async def fetch_application_api_usage( 1812 self, 1813 access_token: str, 1814 application_id: int, 1815 /, 1816 *, 1817 start: datetime.datetime | None = None, 1818 end: datetime.datetime | None = None, 1819 ) -> typedefs.JSONObject: 1820 end_date, start_date = time.parse_date_range(end, start) 1821 resp = await self._request( 1822 _GET, 1823 f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}", 1824 auth=access_token, 1825 ) 1826 assert isinstance(resp, dict) 1827 return resp 1828 1829 async def fetch_bungie_applications(self) -> typedefs.JSONArray: 1830 resp = await self._request(_GET, "App/FirstParty") 1831 assert isinstance(resp, list) 1832 return resp 1833 1834 async def fetch_content_type(self, type: str, /) -> typedefs.JSONObject: 1835 resp = await self._request(_GET, f"Content/GetContentType/{type}/") 1836 assert isinstance(resp, dict) 1837 return resp 1838 1839 async def fetch_content_by_id( 1840 self, id: int, locale: str, /, *, head: bool = False 1841 ) -> typedefs.JSONObject: 1842 resp = await self._request( 1843 _GET, 1844 f"Content/GetContentById/{id}/{locale}/", 1845 json={"head": head}, 1846 ) 1847 assert isinstance(resp, dict) 1848 return resp 1849 1850 async def fetch_content_by_tag_and_type( 1851 self, locale: str, tag: str, type: str, *, head: bool = False 1852 ) -> typedefs.JSONObject: 1853 resp = await self._request( 1854 _GET, 1855 f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/", 1856 json={"head": head}, 1857 ) 1858 assert isinstance(resp, dict) 1859 return resp 1860 1861 async def search_content_with_text( 1862 self, 1863 locale: str, 1864 /, 1865 content_type: str, 1866 search_text: str, 1867 tag: str, 1868 *, 1869 page: int | None = None, 1870 source: str | None = None, 1871 ) -> typedefs.JSONObject: 1872 body: typedefs.JSONObject = { 1873 "locale": locale, 1874 "currentpage": page or 1, 1875 "ctype": content_type, 1876 "searchtxt": search_text, 1877 "searchtext": search_text, 1878 "tag": tag, 1879 "source": source, 1880 } 1881 1882 resp = await self._request(_GET, "Content/Search", params=body) 1883 assert isinstance(resp, dict) 1884 return resp 1885 1886 async def search_content_by_tag_and_type( 1887 self, 1888 locale: str, 1889 tag: str, 1890 type: str, 1891 *, 1892 page: int | None = None, 1893 ) -> typedefs.JSONObject: 1894 body: typedefs.JSONObject = {"currentpage": page or 1} 1895 1896 resp = await self._request( 1897 _GET, 1898 f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/", 1899 params=body, 1900 ) 1901 assert isinstance(resp, dict) 1902 return resp 1903 1904 async def search_help_articles( 1905 self, text: str, size: str, / 1906 ) -> typedefs.JSONObject: 1907 resp = await self._request(_GET, f"Content/SearchHelpArticles/{text}/{size}/") 1908 assert isinstance(resp, dict) 1909 return resp 1910 1911 async def fetch_topics_page( 1912 self, 1913 category_filter: int, 1914 group: int, 1915 date_filter: int, 1916 sort: str | bytes, 1917 *, 1918 page: int | None = None, 1919 locales: collections.Iterable[str] | None = None, 1920 tag_filter: str | None = None, 1921 ) -> typedefs.JSONObject: 1922 params = { 1923 "locales": ",".join(locales) if locales is not None else "en", 1924 } 1925 if tag_filter: 1926 params["tagstring"] = tag_filter 1927 1928 resp = await self._request( 1929 _GET, 1930 f"Forum/GetTopicsPaged/{page or 0}/0/{group}/{sort!s}/{date_filter}/{category_filter}/", 1931 params=params, 1932 ) 1933 assert isinstance(resp, dict) 1934 return resp 1935 1936 async def fetch_core_topics_page( 1937 self, 1938 category_filter: int, 1939 date_filter: int, 1940 sort: str | bytes, 1941 *, 1942 page: int | None = None, 1943 locales: collections.Iterable[str] | None = None, 1944 ) -> typedefs.JSONObject: 1945 resp = await self._request( 1946 _GET, 1947 f"Forum/GetCoreTopicsPaged/{page or 0}" 1948 f"/{sort!s}/{date_filter}/{category_filter}/?locales={','.join(locales) if locales else 'en'}", 1949 ) 1950 assert isinstance(resp, dict) 1951 return resp 1952 1953 async def fetch_posts_threaded_page( 1954 self, 1955 parent_post: bool, 1956 page: int, 1957 page_size: int, 1958 parent_post_id: int, 1959 reply_size: int, 1960 root_thread_mode: bool, 1961 sort_mode: int, 1962 show_banned: str | None = None, 1963 ) -> typedefs.JSONObject: 1964 resp = await self._request( 1965 _GET, 1966 f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/" 1967 f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/", 1968 json={"showbanned": show_banned}, 1969 ) 1970 assert isinstance(resp, dict) 1971 return resp 1972 1973 async def fetch_posts_threaded_page_from_child( 1974 self, 1975 child_id: bool, 1976 page: int, 1977 page_size: int, 1978 reply_size: int, 1979 root_thread_mode: bool, 1980 sort_mode: int, 1981 show_banned: str | None = None, 1982 ) -> typedefs.JSONObject: 1983 resp = await self._request( 1984 _GET, 1985 f"Forum/GetPostsThreadedPagedFromChild/{child_id}/" 1986 f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/", 1987 json={"showbanned": show_banned}, 1988 ) 1989 assert isinstance(resp, dict) 1990 return resp 1991 1992 async def fetch_post_and_parent( 1993 self, child_id: int, /, *, show_banned: str | None = None 1994 ) -> typedefs.JSONObject: 1995 resp = await self._request( 1996 _GET, 1997 f"Forum/GetPostAndParent/{child_id}/", 1998 json={"showbanned": show_banned}, 1999 ) 2000 assert isinstance(resp, dict) 2001 return resp 2002 2003 async def fetch_posts_and_parent_awaiting( 2004 self, child_id: int, /, *, show_banned: str | None = None 2005 ) -> typedefs.JSONObject: 2006 resp = await self._request( 2007 _GET, 2008 f"Forum/GetPostAndParentAwaitingApproval/{child_id}/", 2009 json={"showbanned": show_banned}, 2010 ) 2011 assert isinstance(resp, dict) 2012 return resp 2013 2014 async def fetch_topic_for_content(self, content_id: int, /) -> int: 2015 resp = await self._request(_GET, f"Forum/GetTopicForContent/{content_id}/") 2016 assert isinstance(resp, int) 2017 return resp 2018 2019 async def fetch_forum_tag_suggestions( 2020 self, partial_tag: str, / 2021 ) -> typedefs.JSONObject: 2022 resp = await self._request( 2023 _GET, 2024 "Forum/GetForumTagSuggestions/", 2025 json={"partialtag": partial_tag}, 2026 ) 2027 assert isinstance(resp, dict) 2028 return resp 2029 2030 async def fetch_poll(self, topic_id: int, /) -> typedefs.JSONObject: 2031 resp = await self._request(_GET, f"Forum/Poll/{topic_id}/") 2032 assert isinstance(resp, dict) 2033 return resp 2034 2035 async def fetch_recruitment_thread_summaries(self) -> typedefs.JSONArray: 2036 resp = await self._request(_POST, "Forum/Recruit/Summaries/") 2037 assert isinstance(resp, list) 2038 return resp 2039 2040 async def fetch_recommended_groups( 2041 self, 2042 access_token: str, 2043 /, 2044 *, 2045 date_range: int = 0, 2046 group_type: enums.GroupType | int = enums.GroupType.CLAN, 2047 ) -> typedefs.JSONArray: 2048 resp = await self._request( 2049 _POST, 2050 f"GroupV2/Recommended/{int(group_type)}/{date_range}/", 2051 auth=access_token, 2052 ) 2053 assert isinstance(resp, list) 2054 return resp 2055 2056 async def fetch_available_avatars(self) -> collections.Mapping[str, int]: 2057 resp = await self._request(_GET, "GroupV2/GetAvailableAvatars/") 2058 assert isinstance(resp, dict) 2059 return resp 2060 2061 async def fetch_user_clan_invite_setting( 2062 self, 2063 access_token: str, 2064 /, 2065 membership_type: enums.MembershipType | int, 2066 ) -> bool: 2067 resp = await self._request( 2068 _GET, 2069 f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/", 2070 auth=access_token, 2071 ) 2072 assert isinstance(resp, bool) 2073 return resp 2074 2075 async def fetch_banned_group_members( 2076 self, access_token: str, group_id: int, /, *, page: int = 1 2077 ) -> typedefs.JSONObject: 2078 resp = await self._request( 2079 _GET, 2080 f"GroupV2/{group_id}/Banned/?currentpage={page}", 2081 auth=access_token, 2082 ) 2083 assert isinstance(resp, dict) 2084 return resp 2085 2086 async def fetch_pending_group_memberships( 2087 self, access_token: str, group_id: int, /, *, current_page: int = 1 2088 ) -> typedefs.JSONObject: 2089 resp = await self._request( 2090 _GET, 2091 f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}", 2092 auth=access_token, 2093 ) 2094 assert isinstance(resp, dict) 2095 return resp 2096 2097 async def fetch_invited_group_memberships( 2098 self, access_token: str, group_id: int, /, *, current_page: int = 1 2099 ) -> typedefs.JSONObject: 2100 resp = await self._request( 2101 _GET, 2102 f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}", 2103 auth=access_token, 2104 ) 2105 assert isinstance(resp, dict) 2106 return resp 2107 2108 async def invite_member_to_group( 2109 self, 2110 access_token: str, 2111 /, 2112 group_id: int, 2113 membership_id: int, 2114 membership_type: enums.MembershipType | int, 2115 *, 2116 message: str | None = None, 2117 ) -> typedefs.JSONObject: 2118 resp = await self._request( 2119 _POST, 2120 f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/", 2121 auth=access_token, 2122 json={"message": str(message)}, 2123 ) 2124 assert isinstance(resp, dict) 2125 return resp 2126 2127 async def cancel_group_member_invite( 2128 self, 2129 access_token: str, 2130 /, 2131 group_id: int, 2132 membership_id: int, 2133 membership_type: enums.MembershipType | int, 2134 ) -> typedefs.JSONObject: 2135 resp = await self._request( 2136 _POST, 2137 f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/", 2138 auth=access_token, 2139 ) 2140 assert isinstance(resp, dict) 2141 return resp 2142 2143 async def fetch_historical_definition(self) -> typedefs.JSONObject: 2144 resp = await self._request(_GET, "Destiny2/Stats/Definition/") 2145 assert isinstance(resp, dict) 2146 return resp 2147 2148 async def fetch_historical_stats( 2149 self, 2150 character_id: int, 2151 membership_id: int, 2152 membership_type: enums.MembershipType | int, 2153 day_start: datetime.datetime, 2154 day_end: datetime.datetime, 2155 groups: collections.Sequence[enums.StatsGroupType | int], 2156 modes: collections.Sequence[enums.GameMode | int], 2157 *, 2158 period_type: enums.PeriodType = enums.PeriodType.ALL_TIME, 2159 ) -> typedefs.JSONObject: 2160 end, start = time.parse_date_range(day_end, day_start) 2161 resp = await self._request( 2162 _GET, 2163 f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/", 2164 json={ 2165 "dayend": end, 2166 "daystart": start, 2167 "groups": [str(int(group)) for group in groups], 2168 "modes": [str(int(mode)) for mode in modes], 2169 "periodType": int(period_type), 2170 }, 2171 ) 2172 assert isinstance(resp, dict) 2173 return resp 2174 2175 async def fetch_historical_stats_for_account( 2176 self, 2177 membership_id: int, 2178 membership_type: enums.MembershipType | int, 2179 groups: collections.Sequence[enums.StatsGroupType | int], 2180 ) -> typedefs.JSONObject: 2181 resp = await self._request( 2182 _GET, 2183 f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/", 2184 json={"groups": [str(int(group)) for group in groups]}, 2185 ) 2186 assert isinstance(resp, dict) 2187 return resp 2188 2189 async def fetch_aggregated_activity_stats( 2190 self, 2191 character_id: int, 2192 membership_id: int, 2193 membership_type: enums.MembershipType | int, 2194 /, 2195 ) -> typedefs.JSONObject: 2196 resp = await self._request( 2197 _GET, 2198 f"Destiny2/{int(membership_type)}/Account/{membership_id}/" 2199 f"Character/{character_id}/Stats/AggregateActivityStats/", 2200 ) 2201 assert isinstance(resp, dict) 2202 return resp 2203 2204 async def equip_loadout( 2205 self, 2206 access_token: str, 2207 /, 2208 loadout_index: int, 2209 character_id: int, 2210 membership_type: enums.MembershipType | int, 2211 ) -> None: 2212 response = await self._request( 2213 _POST, 2214 "Destiny2/Actions/Loadouts/EquipLoadout/", 2215 json={ 2216 "loadoutIndex": loadout_index, 2217 "characterId": character_id, 2218 "membership_type": int(membership_type), 2219 }, 2220 auth=access_token, 2221 ) 2222 assert isinstance(response, int) 2223 2224 async def snapshot_loadout( 2225 self, 2226 access_token: str, 2227 /, 2228 loadout_index: int, 2229 character_id: int, 2230 membership_type: enums.MembershipType | int, 2231 *, 2232 color_hash: int | None = None, 2233 icon_hash: int | None = None, 2234 name_hash: int | None = None, 2235 ) -> None: 2236 response = await self._request( 2237 _POST, 2238 "Destiny2/Actions/Loadouts/SnapshotLoadout/", 2239 auth=access_token, 2240 json={ 2241 "colorHash": color_hash, 2242 "iconHash": icon_hash, 2243 "nameHash": name_hash, 2244 "loadoutIndex": loadout_index, 2245 "characterId": character_id, 2246 "membershipType": int(membership_type), 2247 }, 2248 ) 2249 assert isinstance(response, int) 2250 2251 async def update_loadout( 2252 self, 2253 access_token: str, 2254 /, 2255 loadout_index: int, 2256 character_id: int, 2257 membership_type: enums.MembershipType | int, 2258 *, 2259 color_hash: int | None = None, 2260 icon_hash: int | None = None, 2261 name_hash: int | None = None, 2262 ) -> None: 2263 response = await self._request( 2264 _POST, 2265 "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/", 2266 auth=access_token, 2267 json={ 2268 "colorHash": color_hash, 2269 "iconHash": icon_hash, 2270 "nameHash": name_hash, 2271 "loadoutIndex": loadout_index, 2272 "characterId": character_id, 2273 "membershipType": int(membership_type), 2274 }, 2275 ) 2276 assert isinstance(response, int) 2277 2278 async def clear_loadout( 2279 self, 2280 access_token: str, 2281 /, 2282 loadout_index: int, 2283 character_id: int, 2284 membership_type: enums.MembershipType | int, 2285 ) -> None: 2286 response = await self._request( 2287 _POST, 2288 "Destiny2/Actions/Loadouts/ClearLoadout/", 2289 json={ 2290 "loadoutIndex": loadout_index, 2291 "characterId": character_id, 2292 "membership_type": int(membership_type), 2293 }, 2294 auth=access_token, 2295 ) 2296 assert isinstance(response, int)
A single process REST client implementation.
This client is designed to only make HTTP requests and return raw JSON objects.
Example
import aiobungie
client = aiobungie.RESTClient("TOKEN")
async with client:
response = await client.fetch_clan_members(4389205)
for member in response['results']:
print(member['destinyUserInfo'])
Parameters
- token (
str): A valid application token from Bungie's developer portal.
Other Parameters
- max_retries (
int): The max retries number to retry if the request hit a5xxstatus code. - client_secret (
str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client. - client_id (
int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client. - debug (
bool | str): Whether to enable logging responses or not.
Logging Levels
False: This will disable logging.True: This will set the level toDEBUGand enable logging minimal information."TRACE" | TRACE: This will log the response headers along with the minimal information.
364 def __init__( 365 self, 366 token: str, 367 /, 368 *, 369 client_secret: str | None = None, 370 client_id: int | None = None, 371 client_session: aiohttp.ClientSession | None = None, 372 dumps: typedefs.Dumps = helpers.dumps, 373 loads: typedefs.Loads = helpers.loads, 374 max_retries: int = 4, 375 debug: typing.Literal["TRACE"] | bool | int = False, 376 ) -> None: 377 self._session = client_session 378 self._lock: asyncio.Lock | None = None 379 self._client_secret = client_secret 380 self._client_id = client_id 381 self._token: str = token 382 self._max_retries = max_retries 383 self._dumps = dumps 384 self._loads = loads 385 self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {} 386 self.with_debug(debug)
392 @property 393 def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: 394 return self._metadata
A mutable mapping storage for the user's needs.
This mapping is useful for storing any kind of data that the user may need.
Example
import aiobungie
client = aiobungie.RESTClient(…)
async with client:
# Fetch auth tokens and store them
client.metadata["tokens"] = await client.fetch_access_token("code")
# Some other time.
async with client:
# Retrieve the tokens
tokens: aiobungie.OAuth2Response = client.metadata["tokens"]
# Use them to fetch your user.
user = await client.fetch_current_user_memberships(tokens.access_token)
400 @typing.final 401 async def close(self) -> None: 402 if self._session is None: 403 raise RuntimeError("REST client is not running.") 404 405 await self._session.close() 406 self._session = None
Close this REST client session if it was acquired.
This method is automatically called when using async with contextmanager.
Raises
RuntimeError: If the client is already closed.
408 @typing.final 409 def open(self) -> None: 410 """Open a new client session. This is called internally with contextmanager usage.""" 411 if self._session: 412 raise RuntimeError("Cannot open REST client when it's already open.") 413 414 self._session = aiohttp.ClientSession( 415 connector=aiohttp.TCPConnector(), 416 connector_owner=True, 417 raise_for_status=False, 418 timeout=aiohttp.ClientTimeout(total=30.0), 419 )
Open a new client session. This is called internally with contextmanager usage.
421 @typing.final 422 async def static_request( 423 self, 424 method: _HTTP_METHOD, 425 path: str, 426 *, 427 auth: str | None = None, 428 json: collections.Mapping[str, typing.Any] | None = None, 429 ) -> typedefs.JSONIsh: 430 return await self._request(method, path, auth=auth, json=json)
Perform an HTTP request given a valid Bungie endpoint.
Parameters
- method (
str): The request method, This may beGET,POST,PUT, etc. - path (
str): The Bungie endpoint or path. A path must look something like thisDestiny2/3/Profile/46111239123/...
Other Parameters
- auth (
str | None): An optional bearer token for methods that requires OAuth2 Authorization header. - json (
MutableMapping[str, typing.Any] | None): An optional JSON mapping to include in the request.
Returns
aiobungie.typedefs.JSONIsh: The response payload.
438 @typing.final 439 def build_oauth2_url( 440 self, client_id: int | None = None 441 ) -> builders.OAuthURL | None: 442 client_id = client_id or self._client_id 443 if client_id is None: 444 return None 445 446 return builders.OAuthURL(client_id=client_id)
Builds an OAuth2 URL using the provided user REST/Base client secret/id.
You can't get the complete string URL by using .compile() method.
Parameters
- client_id (
int | None): An optional client id to provide, If leftNoneit will roll back to the id passed to theRESTClient, If both isNonethis method will returnNone.
Returns
aiobungie.builders.OAuthURL | None: If the client id was provided as a parameter or provided inaiobungie.RESTClient, A complete OAuthURL object will be returned. OtherwiseNonewill be returned.
448 @typing.final 449 def with_debug( 450 self, 451 level: typing.Literal["TRACE"] | bool | int = True, 452 file: str | pathlib.Path | None = None, 453 ) -> None: 454 """Enable debugging for this client with a level. Defaults to `True`. 455 456 Parameters 457 ---------- 458 level: `NotRequired[int | bool | typing.Literal["TRACE"] | None]` 459 The level of the logger. This field is not required. 460 file: `pathlib.Path | str | None` 461 An optional file to write the logs into. 462 463 Logging Levels 464 -------------- 465 * `False`: This will disable logging. 466 * `True`: This will set the level to `DEBUG` and enable logging minimal information. 467 * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information. 468 """ 469 logging.logThreads = False 470 logging.logMultiprocessing = False 471 logging.logProcesses = False 472 logging.captureWarnings(True) 473 474 format = "%(levelname)s " "%(asctime)23.23s " "%(name)s: " "%(message)s" 475 476 file_handler = (logging.FileHandler(file),) if file else None 477 if level == "TRACE" or level == TRACE: 478 logging.basicConfig( 479 level=TRACE, format=format, stream=sys.stdout, handlers=file_handler 480 ) 481 482 elif level is True: 483 logging.basicConfig( 484 level=logging.DEBUG, 485 format=format, 486 stream=sys.stdout, 487 handlers=file_handler, 488 )
Enable debugging for this client with a level. Defaults to True.
Parameters
- level (
NotRequired[int | bool | typing.Literal["TRACE"] | None]): The level of the logger. This field is not required. - file (
pathlib.Path | str | None): An optional file to write the logs into.
Logging Levels
False: This will disable logging.True: This will set the level toDEBUGand enable logging minimal information."TRACE" | TRACE: This will log the response headers along with the minimal information.
714 async def fetch_oauth2_tokens(self, code: str, /) -> builders.OAuth2Response: 715 data = { 716 "grant_type": "authorization_code", 717 "code": code, 718 "client_id": self._client_id, 719 "client_secret": self._client_secret, 720 } 721 722 response = await self._request(_POST, "", data=data, oauth2=True) 723 assert isinstance(response, dict) 724 return builders.OAuth2Response.build_response(response)
Makes a POST request and fetch the OAuth2 access_token and refresh token.
Parameters
- code (
str): The Authorization code received from the authorization endpoint found in the URL parameters.
Returns
aiobungie.builders.OAuth2Response: An OAuth2 deserialized response.
Raises
Unauthorized: The passed code was invalid.
726 async def refresh_access_token( 727 self, refresh_token: str, / 728 ) -> builders.OAuth2Response: 729 data = { 730 "grant_type": "refresh_token", 731 "refresh_token": refresh_token, 732 "client_id": self._client_id, 733 "client_secret": self._client_secret, 734 } 735 736 response = await self._request(_POST, "", data=data, oauth2=True) 737 assert isinstance(response, dict) 738 return builders.OAuth2Response.build_response(response)
Refresh OAuth2 access token given its refresh token.
Parameters
- refresh_token (
str): The refresh token.
Returns
aiobungie.builders.OAuth2Response: An OAuth2 deserialized response.
740 async def fetch_bungie_user(self, id: int) -> typedefs.JSONObject: 741 resp = await self._request(_GET, f"User/GetBungieNetUserById/{id}/") 742 assert isinstance(resp, dict) 743 return resp
Fetch a Bungie user by their id.
Parameters
- id (
int): The user id.
Returns
aiobungie.typedefs.JSONObject: A JSON object of users objects.
Raises
NotFound: The user was not found.
750 async def fetch_membership_from_id( 751 self, 752 id: int, 753 type: enums.MembershipType | int = enums.MembershipType.NONE, 754 /, 755 ) -> typedefs.JSONObject: 756 resp = await self._request(_GET, f"User/GetMembershipsById/{id}/{int(type)}") 757 assert isinstance(resp, dict) 758 return resp
Fetch Bungie user's memberships from their id.
Parameters
- id (
int): The user's id. - type (
aiobungie.aiobungie.MembershipType | int): The user's membership type.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the found user.
Raises
- aiobungie.NotFound: The requested user was not found.
760 async def fetch_membership( 761 self, 762 name: str, 763 code: int, 764 type: enums.MembershipType | int = enums.MembershipType.ALL, 765 /, 766 ) -> typedefs.JSONArray: 767 resp = await self._request( 768 _POST, 769 f"Destiny2/SearchDestinyPlayerByBungieName/{int(type)}", 770 json={"displayName": name, "displayNameCode": code}, 771 ) 772 assert isinstance(resp, list) 773 return resp
Fetch a Destiny 2 Player.
Parameters
- name (
str): The unique Bungie player name. - code (
int): The unique Bungie display name code. - type (
aiobungie.aiobungie.MembershipType | int): The player's membership type, e,g. XBOX, STEAM, PSN
Returns
aiobungie.typedefs.JSONArray: A JSON array of the found player's memberships.
Raises
aiobungie.NotFound: The player was not found.aiobungie.MembershipTypeError: The provided membership type was invalid.
775 async def search_users(self, name: str, /) -> typedefs.JSONObject: 776 resp = await self._request( 777 _POST, 778 "User/Search/GlobalName/0", 779 json={"displayNamePrefix": name}, 780 ) 781 assert isinstance(resp, dict) 782 return resp
Search for users by their global name and return all users who share this name.
Parameters
- name (
str): The user name.
Returns
aiobungie.typedefs.JSONObject: A JSON object of an array of the found users.
Raises
aiobungie.NotFound: The user(s) was not found.
784 async def fetch_clan_from_id( 785 self, id: int, /, access_token: str | None = None 786 ) -> typedefs.JSONObject: 787 resp = await self._request(_GET, f"GroupV2/{id}", auth=access_token) 788 assert isinstance(resp, dict) 789 return resp
Fetch a Bungie Clan by its id.
Parameters
- id (
int): The clan id.
Other Parameters
access_token (
str | None): An optional access token to make the request with.If the token was bound to a member of the clan, This field
aiobungie.crates.Clan.current_user_membershipwill be available and will return the membership of the user who made this request.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the clan.
Raises
aiobungie.NotFound: The clan was not found.
791 async def fetch_clan( 792 self, 793 name: str, 794 /, 795 access_token: str | None = None, 796 *, 797 type: enums.GroupType | int = enums.GroupType.CLAN, 798 ) -> typedefs.JSONObject: 799 resp = await self._request( 800 _GET, f"GroupV2/Name/{name}/{int(type)}", auth=access_token 801 ) 802 assert isinstance(resp, dict) 803 return resp
Fetch a Clan by its name. This method will return the first clan found with given name name.
Parameters
- name (
str): The clan name.
Other Parameters
access_token (
str | None): An optional access token to make the request with.If the token was bound to a member of the clan, This field
aiobungie.crates.Clan.current_user_membershipwill be available and will return the membership of the user who made this request.- type (
aiobungie.aiobungie.GroupType | int): The group type, Default is one.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the clan.
Raises
aiobungie.NotFound: The clan was not found.
805 async def fetch_clan_admins(self, clan_id: int, /) -> typedefs.JSONObject: 806 resp = await self._request(_GET, f"GroupV2/{clan_id}/AdminsAndFounder/") 807 assert isinstance(resp, dict) 808 return resp
Fetch the admins and founder members of the clan.
Parameters
- clan_id (
int): The clan id.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the clan admins and founder members.
Raises
aiobungie.NotFound: The clan was not found.
810 async def fetch_clan_conversations(self, clan_id: int, /) -> typedefs.JSONArray: 811 resp = await self._request(_GET, f"GroupV2/{clan_id}/OptionalConversations/") 812 assert isinstance(resp, list) 813 return resp
Fetch a clan's conversations.
Parameters
- clan_id (
int): The clan's id.
Returns
aiobungie.typedefs.JSONArray: A JSON array of the conversations.
815 async def fetch_application(self, appid: int, /) -> typedefs.JSONObject: 816 resp = await self._request(_GET, f"App/Application/{appid}") 817 assert isinstance(resp, dict) 818 return resp
Fetch a Bungie Application.
Parameters
- appid (
int): The application id.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the application.
820 async def fetch_character( 821 self, 822 member_id: int, 823 membership_type: enums.MembershipType | int, 824 character_id: int, 825 components: collections.Sequence[enums.ComponentType], 826 auth: str | None = None, 827 ) -> typedefs.JSONObject: 828 collector = _collect_components(components) 829 response = await self._request( 830 _GET, 831 f"Destiny2/{int(membership_type)}/Profile/{member_id}/" 832 f"Character/{character_id}/?components={collector}", 833 auth=auth, 834 ) 835 assert isinstance(response, dict) 836 return response
Fetch a Destiny 2 player's characters.
Parameters
- member_id (
int): A valid bungie member id. - membership_type (
aiobungie.aiobungie.internal.enums.MembershipType | int): The member's membership type. - character_id (
int): The character id to return. - components (
collections.Sequence[aiobungie.ComponentType]): A list of character components to collect and return.
Other Parameters
- auth (
str | None): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the requested character.
Raises
aiobungie.MembershipTypeError: The provided membership type was invalid.
838 async def fetch_activities( 839 self, 840 member_id: int, 841 character_id: int, 842 mode: enums.GameMode | int, 843 membership_type: enums.MembershipType | int = enums.MembershipType.ALL, 844 *, 845 page: int = 0, 846 limit: int = 1, 847 ) -> typedefs.JSONObject: 848 resp = await self._request( 849 _GET, 850 f"Destiny2/{int(membership_type)}/Account/" 851 f"{member_id}/Character/{character_id}/Stats/Activities" 852 f"/?mode={int(mode)}&count={limit}&page={page}", 853 ) 854 assert isinstance(resp, dict) 855 return resp
Fetch a Destiny 2 activity for the specified user id and character.
Parameters
- member_id (
int): The user id that starts with4611. - character_id (
int): The id of the character to retrieve. - mode (
aiobungie.aiobungie.GameMode | int): This parameter filters the game mode, Nightfall, Strike, Iron Banner, etc.
Other Parameters
- membership_type (
aiobungie.aiobungie.internal.enums.MembershipType | int): The Member ship type, if nothing was passed than it will return all. - page (
int): The page number. Default to1 - limit (
int): Limit the returned result. Default to1
Returns
aiobungie.typedefs.JSONObject: A JSON object of the player's activities.
Raises
NotFound: The activity was not found.aiobungie.MembershipTypeError: The provided membership type was invalid.
865 async def fetch_profile( 866 self, 867 membership_id: int, 868 type: enums.MembershipType | int, 869 components: collections.Sequence[enums.ComponentType], 870 auth: str | None = None, 871 ) -> typedefs.JSONObject: 872 collector = _collect_components(components) 873 response = await self._request( 874 _GET, 875 f"Destiny2/{int(type)}/Profile/{membership_id}/?components={collector}", 876 auth=auth, 877 ) 878 assert isinstance(response, dict) 879 return response
Fetch a bungie profile.
Parameters
- membership_id (
int): The member's id. - type (
aiobungie.aiobungie.MembershipType | int): A valid membership type. - components (
collections.Sequence[aiobungie.ComponentType]): A sequence of profile components to collect and return.
Other Parameters
- auth (
str | None): A bearer access_token to make the request with. This is optional and limited to components that only requires an Authorization token.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the found profile.
Raises
aiobungie.MembershipTypeError: The provided membership type was invalid.
881 async def fetch_entity(self, type: str, hash: int) -> typedefs.JSONObject: 882 response = await self._request(_GET, route=f"Destiny2/Manifest/{type}/{hash}") 883 assert isinstance(response, dict) 884 return response
Fetch a Destiny definition item given its type and hash.
Parameters
- type (
str): Entity's type definition. - hash (
int): Entity's hash.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the definition data.
886 async def fetch_inventory_item(self, hash: int, /) -> typedefs.JSONObject: 887 resp = await self.fetch_entity("DestinyInventoryItemDefinition", hash) 888 assert isinstance(resp, dict) 889 return resp
Fetch a Destiny inventory item entity given a its hash.
Parameters
- hash (
int): Entity's hash.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the inventory item.
891 async def fetch_objective_entity(self, hash: int, /) -> typedefs.JSONObject: 892 resp = await self.fetch_entity("DestinyObjectiveDefinition", hash) 893 assert isinstance(resp, dict) 894 return resp
Fetch a Destiny objective entity given a its hash.
Parameters
- hash (
int): objective's hash.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the objective data.
896 async def fetch_groups_for_member( 897 self, 898 member_id: int, 899 member_type: enums.MembershipType | int, 900 /, 901 *, 902 filter: int = 0, 903 group_type: enums.GroupType | int = enums.GroupType.CLAN, 904 ) -> typedefs.JSONObject: 905 resp = await self._request( 906 _GET, 907 f"GroupV2/User/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/", 908 ) 909 assert isinstance(resp, dict) 910 return resp
Fetch the information about the groups for a member.
Parameters
- member_id (
int): The member's id - member_type (
aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
- filter (
int): Filter apply to list of joined groups. This Default to0 - group_type (
aiobungie.aiobungie.GroupType | int): The group's type. This is always set toaiobungie.GroupType.CLANand should not be changed.
Returns
aiobungie.typedefs.JSONObject: A JSON object of an array of the member's membership data and groups data.
912 async def fetch_potential_groups_for_member( 913 self, 914 member_id: int, 915 member_type: enums.MembershipType | int, 916 /, 917 *, 918 filter: int = 0, 919 group_type: enums.GroupType | int = enums.GroupType.CLAN, 920 ) -> typedefs.JSONObject: 921 resp = await self._request( 922 _GET, 923 f"GroupV2/User/Potential/{int(member_type)}/{member_id}/{filter}/{int(group_type)}/", 924 ) 925 assert isinstance(resp, dict) 926 return resp
Get information about the groups that a given member has applied to or been invited to.
Parameters
- member_id (
int): The member's id - member_type (
aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
- filter (
int): Filter apply to list of joined groups. This Default to0 - group_type (
aiobungie.aiobungie.GroupType | int): The group's type. This is always set toaiobungie.GroupType.CLANand should not be changed.
Returns
aiobungie.typedefs.JSONObject: A JSON object of an array of the member's membership data and groups data.
928 async def fetch_clan_members( 929 self, 930 clan_id: int, 931 /, 932 *, 933 name: str | None = None, 934 type: enums.MembershipType | int = enums.MembershipType.NONE, 935 ) -> typedefs.JSONObject: 936 resp = await self._request( 937 _GET, 938 f"/GroupV2/{clan_id}/Members/?memberType={int(type)}&nameSearch={name if name else ''}¤tpage=1", 939 ) 940 assert isinstance(resp, dict) 941 return resp
Fetch all Bungie Clan members.
Parameters
- clan_id (
int): The clans id
Other Parameters
- name (
str | None): If provided, Only players matching this name will be returned. - type (
aiobungie.aiobungie.MembershipType | int): An optional clan member's membership type. Default is set toaiobungie.MembershipType.NONEWhich returns the first matched clan member by their name.
Returns
aiobungie.typedefs.JSONObject: A JSON object of an array of clan members.
Raises
aiobungie.NotFound: The clan was not found.
943 async def fetch_hardlinked_credentials( 944 self, 945 credential: int, 946 type: enums.CredentialType | int = enums.CredentialType.STEAMID, 947 /, 948 ) -> typedefs.JSONObject: 949 resp = await self._request( 950 _GET, 951 f"User/GetMembershipFromHardLinkedCredential/{int(type)}/{credential}/", 952 ) 953 assert isinstance(resp, dict) 954 return resp
Gets any hard linked membership given a credential.
Only works for credentials that are public just aiobungie.CredentialType.STEAMID right now.
Cross Save aware.
Parameters
- credential (
int): A valid SteamID64 - type (
aiobungie.aiobungie.CredentialType | int): The credential type. This must not be changed Since its only credential that works "currently"
Returns
aiobungie.typedefs.JSONObject: A JSON object of the found user hard linked types.
956 async def fetch_user_credentials( 957 self, access_token: str, membership_id: int, / 958 ) -> typedefs.JSONArray: 959 resp = await self._request( 960 _GET, 961 f"User/GetCredentialTypesForTargetAccount/{membership_id}", 962 auth=access_token, 963 ) 964 assert isinstance(resp, list) 965 return resp
Fetch an array of credential types attached to the requested account.
This method require OAuth2 Bearer access token.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - membership_id (
int): The id of the membership to return.
Returns
aiobungie.typedefs.JSONArray: A JSON array of the returned credentials.
Raises
aiobungie.Unauthorized: The access token was wrong or no access token passed.
967 async def insert_socket_plug( 968 self, 969 action_token: str, 970 /, 971 instance_id: int, 972 plug: builders.PlugSocketBuilder | collections.Mapping[str, int], 973 character_id: int, 974 membership_type: enums.MembershipType | int, 975 ) -> typedefs.JSONObject: 976 if isinstance(plug, builders.PlugSocketBuilder): 977 plug = plug.collect() 978 979 body = { 980 "actionToken": action_token, 981 "itemInstanceId": instance_id, 982 "plug": plug, 983 "characterId": character_id, 984 "membershipType": int(membership_type), 985 } 986 resp = await self._request( 987 _POST, "Destiny2/Actions/Items/InsertSocketPlug", json=body 988 ) 989 assert isinstance(resp, dict) 990 return resp
Insert a plug into a socketed item.
OAuth2: AdvancedWriteActions scope is required
Parameters
- action_token (
str): Action token provided by the AwaGetActionToken API call. - instance_id (
int): The item instance id that's plug inserted. - plug (
aiobungie.builders.PlugSocketBuilder | Mapping[str, int]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
aiobungie.PlugSocketBuilder()
.set_socket_array(0)
.set_socket_index(0)
.set_plug_item(3023847)
.collect()
)
await insert_socket_plug_free(..., plug=plug)
character_id : int
The character's id.
membership_type : aiobungie.aiobungie.MembershipType | int
The membership type.
Returns
aiobungie.typedefs.JSONObject: A JSON object contains the changed item details.
Raises
aiobungie.Unauthorized: The access token was wrong or no access token passed.
992 async def insert_socket_plug_free( 993 self, 994 access_token: str, 995 /, 996 instance_id: int, 997 plug: builders.PlugSocketBuilder | collections.Mapping[str, int], 998 character_id: int, 999 membership_type: enums.MembershipType | int, 1000 ) -> typedefs.JSONObject: 1001 if isinstance(plug, builders.PlugSocketBuilder): 1002 plug = plug.collect() 1003 1004 body = { 1005 "itemInstanceId": instance_id, 1006 "plug": plug, 1007 "characterId": character_id, 1008 "membershipType": int(membership_type), 1009 } 1010 resp = await self._request( 1011 _POST, 1012 "Destiny2/Actions/Items/InsertSocketPlugFree", 1013 json=body, 1014 auth=access_token, 1015 ) 1016 assert isinstance(resp, dict) 1017 return resp
Insert a plug into a socketed item. This doesn't require an Action token.
OAuth2: MoveEquipDestinyItems scope is required
Parameters
- instance_id (
int): The item instance id that's plug inserted. - plug (
aiobungie.builders.PlugSocketBuilder | Mapping[str, int]): Either a PlugSocketBuilder object or a raw dict contains key, value for the plug entries.
Example
plug = (
aiobungie.PlugSocketBuilder()
.set_socket_array(0)
.set_socket_index(0)
.set_plug_item(3023847)
.collect()
)
await insert_socket_plug_free(..., plug=plug)
character_id : int
The character's id.
membership_type : aiobungie.aiobungie.MembershipType | int
The membership type.
Returns
aiobungie.typedefs.JSONObject: A JSON object contains the changed item details.
Raises
aiobungie.Unauthorized: The access token was wrong or no access token passed.
1019 @helpers.unstable 1020 async def set_item_lock_state( 1021 self, 1022 access_token: str, 1023 state: bool, 1024 /, 1025 item_id: int, 1026 character_id: int, 1027 membership_type: enums.MembershipType | int, 1028 ) -> int: 1029 body = { 1030 "state": state, 1031 "itemId": item_id, 1032 "characterId": character_id, 1033 "membershipType": int(membership_type), 1034 } 1035 response = await self._request( 1036 _POST, 1037 "Destiny2/Actions/Items/SetLockState", 1038 json=body, 1039 auth=access_token, 1040 ) 1041 assert isinstance(response, int) 1042 return response
Set the Lock State for an instanced item.
OAuth2: MoveEquipDestinyItems scope is required
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - state (
bool): IfTrue, The item will be locked, IfFalse, The item will be unlocked. - item_id (
int): The item id. - character_id (
int): The character id. - membership_type (
aiobungie.aiobungie.MembershipType | int): The membership type for the associated account.
Returns
int: An integer represents whether the request was successful or failed.
Raises
aiobungie.Unauthorized: - The access token was wrong- No access token passed.
- Other authorization causes.
1044 async def set_quest_track_state( 1045 self, 1046 access_token: str, 1047 state: bool, 1048 /, 1049 item_id: int, 1050 character_id: int, 1051 membership_type: enums.MembershipType | int, 1052 ) -> int: 1053 body = { 1054 "state": state, 1055 "itemId": item_id, 1056 "characterId": character_id, 1057 "membership_type": int(membership_type), 1058 } 1059 response = await self._request( 1060 _POST, 1061 "Destiny2/Actions/Items/SetTrackedState", 1062 json=body, 1063 auth=access_token, 1064 ) 1065 assert isinstance(response, int) 1066 return response
Set the Tracking State for an instanced Quest or Bounty.
OAuth2: MoveEquipDestinyItems scope is required
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - state (
bool): IfTrue, The item will be locked, IfFalse, The item will be unlocked. - item_id (
int): The item id. - character_id (
int): The character id. - membership_type (
aiobungie.aiobungie.MembershipType | int): The membership type for the associated account.
Returns
int: An integer represents whether the request was successful or failed.
Raises
aiobungie.Unauthorized: - The access token was wrong- No access token passed.
- Other authorization causes.
1068 async def fetch_manifest_path(self) -> typedefs.JSONObject: 1069 path = await self._request(_GET, "Destiny2/Manifest") 1070 assert isinstance(path, dict) 1071 return path
Fetch the manifest JSON paths.
Returns
typedefs.JSONObject: The manifest JSON paths.
1073 async def read_manifest_bytes(self, language: str = "en", /) -> bytes: 1074 _ensure_manifest_language(language) 1075 1076 content = await self.fetch_manifest_path() 1077 resp = await self._request( 1078 _GET, 1079 content["mobileWorldContentPaths"][language], 1080 unwrap_bytes=True, 1081 base=True, 1082 ) 1083 assert isinstance(resp, bytes) 1084 return resp
Read raw manifest SQLite database bytes response.
This method can be used to write the bytes to zipped file and then extract it to get the manifest content.
Parameters
- language (
str): The manifest database language bytes to get.
Returns
bytes: The bytes to read and write the manifest database.
1086 async def download_sqlite_manifest( 1087 self, 1088 language: str = "en", 1089 name: str = "manifest", 1090 path: pathlib.Path | str = ".", 1091 *, 1092 force: bool = False, 1093 executor: concurrent.futures.Executor | None = None, 1094 ) -> pathlib.Path: 1095 complete_path = _get_path(name, path, sql=True) 1096 1097 if complete_path.exists() and force: 1098 if force: 1099 _LOGGER.info( 1100 f"Found manifest in {complete_path!s}. Forcing to Re-Download." 1101 ) 1102 complete_path.unlink(missing_ok=True) 1103 1104 return await self.download_sqlite_manifest( 1105 language, name, path, force=force 1106 ) 1107 1108 else: 1109 raise FileExistsError( 1110 "Manifest file already exists, " 1111 "To force download, set the `force` parameter to `True`." 1112 ) 1113 1114 _LOGGER.info(f"Downloading manifest. Location: {complete_path!s}") 1115 data_bytes = await self.read_manifest_bytes(language) 1116 await asyncio.get_running_loop().run_in_executor( 1117 executor, _write_sqlite_bytes, data_bytes, path, name 1118 ) 1119 _LOGGER.info("Finished downloading manifest.") 1120 return _get_path(name, path, sql=True)
Downloads the SQLite version of Destiny2's Manifest.
Example
manifest = await rest.download_sqlite_manifest()
with sqlite3.connect(manifest) as conn:
...
Parameters
- language (
str): The manifest language to download, Default is English. - path (
str|pathlib.Path): The path to download this manifest. Example"/tmp/databases/", Default is the current directory. - name (
str): The manifest database file name. Default ismanifest - force (
bool): Whether to force the download. Default isFalse. However if set to true the old file will get unlinked and a new one will begin to download. - executor (
concurrent.futures.Executor | None): An optional executor which will be used to write the bytes of the manifest.
Returns
pathlib.Path: A pathlib object of the.sqlitefile.
Raises
FileNotFoundError: If the manifest file exists andforceisFalse.ValueError: If the provided language was not recognized.
1122 async def download_json_manifest( 1123 self, 1124 file_name: str = "manifest", 1125 path: str | pathlib.Path = ".", 1126 *, 1127 language: str = "en", 1128 executor: concurrent.futures.Executor | None = None, 1129 ) -> pathlib.Path: 1130 _ensure_manifest_language(language) 1131 full_path = _get_path(file_name, path) 1132 _LOGGER.info(f"Downloading manifest JSON to {full_path!r}...") 1133 1134 content = await self.fetch_manifest_path() 1135 json_bytes = await self._request( 1136 _GET, 1137 content["jsonWorldContentPaths"][language], 1138 unwrap_bytes=True, 1139 base=True, 1140 ) 1141 1142 assert isinstance(json_bytes, bytes) 1143 await asyncio.get_running_loop().run_in_executor( 1144 executor, _write_json_bytes, json_bytes, file_name, path 1145 ) 1146 _LOGGER.info("Finished downloading manifest JSON.") 1147 return full_path
Download the Bungie manifest json file.
Example
manifest = await rest.download_json_manifest()
with open(manifest, "r") as f:
to_dict = json.loads(f.read())
item_definitions = to_dict['DestinyInventoryItemDefinition']
Parameters
- file_name (
str): The file name to save the manifest json file. Default ismanifest. - path (
str|pathlib.Path): The path to save the manifest json file. Default is the current directory. Example"D:/" - language (
str): The manifest database language bytes to get. Default is English. - executor (
concurrent.futures.Executor | None): An optional executor which will be used to write the bytes of the manifest.
Returns
pathlib.Path: The path of this JSON manifest.
1149 async def fetch_manifest_version(self) -> str: 1150 # This is guaranteed str. 1151 return (await self.fetch_manifest_path())["version"]
Fetch the manifest version.
Returns
str: The manifest version.
1153 async def fetch_linked_profiles( 1154 self, 1155 member_id: int, 1156 member_type: enums.MembershipType | int, 1157 /, 1158 *, 1159 all: bool = False, 1160 ) -> typedefs.JSONObject: 1161 resp = await self._request( 1162 _GET, 1163 f"Destiny2/{int(member_type)}/Profile/{member_id}/LinkedProfiles/?getAllMemberships={all}", 1164 ) 1165 assert isinstance(resp, dict) 1166 return resp
Returns a summary information about all profiles linked to the requested member.
The passed membership id/type maybe a Bungie.Net membership or a Destiny memberships.
It will only return linked accounts whose linkages you are allowed to view.
Parameters
- member_id (
int): The ID of the membership. This must be a valid Bungie.Net or PSN or Xbox ID. - member_type (
aiobungie.aiobungie.MembershipType | int): The type for the membership whose linked Destiny account you want to return.
Other Parameters
all (
bool): If provided and set toTrue, All memberships regardless of whether they're obscured by overrides will be returned,If provided and set to
False, Only available memberships will be returned. The default for this isFalse.
Returns
aiobungie.typedefs.JSONObject- A JSON object which contains an Array of profiles, an Array of profiles with errors and Bungie.Net membership
1173 async def fetch_public_milestones(self) -> typedefs.JSONObject: 1174 resp = await self._request(_GET, "Destiny2/Milestones/") 1175 assert isinstance(resp, dict) 1176 return resp
Fetch the available milestones.
Returns
aiobungie.typedefs.JSONObject: A JSON object of information about the milestones.
1178 async def fetch_public_milestone_content( 1179 self, milestone_hash: int, / 1180 ) -> typedefs.JSONObject: 1181 resp = await self._request( 1182 _GET, f"Destiny2/Milestones/{milestone_hash}/Content/" 1183 ) 1184 assert isinstance(resp, dict) 1185 return resp
Fetch the milestone content given its hash.
Parameters
- milestone_hash (
int): The milestone hash.
Returns
aiobungie.typedefs.JSONObject: A JSON object of information related to the fetched milestone.
1187 async def fetch_current_user_memberships( 1188 self, access_token: str, / 1189 ) -> typedefs.JSONObject: 1190 resp = await self._request( 1191 _GET, 1192 "User/GetMembershipsForCurrentUser/", 1193 auth=access_token, 1194 ) 1195 assert isinstance(resp, dict) 1196 return resp
Fetch a bungie user's accounts with the signed in user. This GET method requires a Bearer access token for the authorization.
This requires OAuth2 scope enabled and the valid Bearer access_token.
Parameters
- access_token (
str): The bearer access token associated with the bungie account.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the bungie net user and destiny memberships of this account.
1198 async def equip_item( 1199 self, 1200 access_token: str, 1201 /, 1202 item_id: int, 1203 character_id: int, 1204 membership_type: enums.MembershipType | int, 1205 ) -> None: 1206 payload = { 1207 "itemId": item_id, 1208 "characterId": character_id, 1209 "membershipType": int(membership_type), 1210 } 1211 1212 await self._request( 1213 _POST, 1214 "Destiny2/Actions/Items/EquipItem/", 1215 json=payload, 1216 auth=access_token, 1217 )
Equip an item to a character.
This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - item_id (
int): The item id. - character_id (
int): The character's id to equip the item to. - membership_type (
aiobungie.aiobungie.MembershipType | int): The membership type associated with this player.
1219 async def equip_items( 1220 self, 1221 access_token: str, 1222 /, 1223 item_ids: collections.Sequence[int], 1224 character_id: int, 1225 membership_type: enums.MembershipType | int, 1226 ) -> None: 1227 payload = { 1228 "itemIds": item_ids, 1229 "characterId": character_id, 1230 "membershipType": int(membership_type), 1231 } 1232 await self._request( 1233 _POST, 1234 "Destiny2/Actions/Items/EquipItems/", 1235 json=payload, 1236 auth=access_token, 1237 )
Equip multiple items to a character.
This requires the OAuth2: MoveEquipDestinyItems scope. Also You must have a valid Destiny account, and either be in a social space, in orbit or offline.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - item_ids (
Sequence[int]): A sequence of item ids. - character_id (
int): The character's id to equip the item to. - membership_type (
aiobungie.aiobungie.MembershipType | int): The membership type associated with this player.
1239 async def ban_clan_member( 1240 self, 1241 access_token: str, 1242 /, 1243 group_id: int, 1244 membership_id: int, 1245 membership_type: enums.MembershipType | int, 1246 *, 1247 length: int = 0, 1248 comment: str | None = None, 1249 ) -> None: 1250 payload = {"comment": str(comment), "length": length} 1251 await self._request( 1252 _POST, 1253 f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Ban/", 1254 json=payload, 1255 auth=access_token, 1256 )
Bans a member from the clan.
This request requires OAuth2: oauth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group id. - membership_id (
int): The member id to ban. - membership_type (
aiobungie.aiobungie.MembershipType | int): The member's membership type.
Other Parameters
- length (
int): An optional ban length. - comment (
aiobungie.UndefinedOr[str]): An optional comment to this ban. Default isUNDEFINED
1258 async def unban_clan_member( 1259 self, 1260 access_token: str, 1261 /, 1262 group_id: int, 1263 membership_id: int, 1264 membership_type: enums.MembershipType | int, 1265 ) -> None: 1266 await self._request( 1267 _POST, 1268 f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Unban/", 1269 auth=access_token, 1270 )
Unban a member from the clan.
This request requires OAuth2: oauth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group id. - membership_id (
int): The member id to unban. - membership_type (
aiobungie.aiobungie.MembershipType | int): The member's membership type.
1272 async def kick_clan_member( 1273 self, 1274 access_token: str, 1275 /, 1276 group_id: int, 1277 membership_id: int, 1278 membership_type: enums.MembershipType | int, 1279 ) -> typedefs.JSONObject: 1280 resp = await self._request( 1281 _POST, 1282 f"GroupV2/{group_id}/Members/{int(membership_type)}/{membership_id}/Kick/", 1283 auth=access_token, 1284 ) 1285 assert isinstance(resp, dict) 1286 return resp
Kick a member from the clan.
This request requires OAuth2: oauth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group id. - membership_id (
int): The member id to kick. - membership_type (
aiobungie.aiobungie.MembershipType | int): The member's membership type.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the group that the member has been kicked from.
1288 async def edit_clan( 1289 self, 1290 access_token: str, 1291 /, 1292 group_id: int, 1293 *, 1294 name: str | None = None, 1295 about: str | None = None, 1296 motto: str | None = None, 1297 theme: str | None = None, 1298 tags: collections.Sequence[str] | None = None, 1299 is_public: bool | None = None, 1300 locale: str | None = None, 1301 avatar_image_index: int | None = None, 1302 membership_option: enums.MembershipOption | int | None = None, 1303 allow_chat: bool | None = None, 1304 chat_security: typing.Literal[0, 1] | None = None, 1305 call_sign: str | None = None, 1306 homepage: typing.Literal[0, 1, 2] | None = None, 1307 enable_invite_messaging_for_admins: bool | None = None, 1308 default_publicity: typing.Literal[0, 1, 2] | None = None, 1309 is_public_topic_admin: bool | None = None, 1310 ) -> None: 1311 payload = { 1312 "name": name, 1313 "about": about, 1314 "motto": motto, 1315 "theme": theme, 1316 "tags": tags, 1317 "isPublic": is_public, 1318 "avatarImageIndex": avatar_image_index, 1319 "isPublicTopicAdminOnly": is_public_topic_admin, 1320 "allowChat": allow_chat, 1321 "chatSecurity": chat_security, 1322 "callsign": call_sign, 1323 "homepage": homepage, 1324 "enableInvitationMessagingForAdmins": enable_invite_messaging_for_admins, 1325 "defaultPublicity": default_publicity, 1326 "locale": locale, 1327 } 1328 if membership_option is not None: 1329 payload["membershipOption"] = int(membership_option) 1330 1331 await self._request( 1332 _POST, 1333 f"GroupV2/{group_id}/Edit", 1334 json=payload, 1335 auth=access_token, 1336 )
Edit a clan.
Notes
- This request requires OAuth2: oauth2:
AdminGroupsscope. - All arguments will default to
Noneif not provided. This does not includeaccess_tokenandgroup_id
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group id to edit.
Other Parameters
- name (
str | None): The name to edit the clan with. - about (
str | None): The about section to edit the clan with. - motto (
str | None): The motto section to edit the clan with. - theme (
str | None): The theme name to edit the clan with. - tags (
collections.Sequence[str] | None): A sequence of strings to replace the clan tags with. - is_public (
bool | None): If provided and set toTrue, The clan will set to private. If provided and set toFalse, The clan will set to public whether it was or not. - locale (
str | None): The locale section to edit the clan with. - avatar_image_index (
int | None): The clan avatar image index to edit the clan with. - membership_option :
aiobungie.typedefs.NoneOr[aiobungie.aiobungie.MembershipOption | int]# noqa (E501 # Line too long): The clan membership option to edit it with. - allow_chat (
bool | None): If provided and set toTrue, The clan members will be allowed to chat. If provided and set toFalse, The clan members will not be allowed to chat. - chat_security (
aiobungie.typedefs.NoneOr[typing.Literal[0, 1]]): If provided and set to0, The clan chat security will be edited toGrouponly. If provided and set to1, The clan chat security will be edited toAdminonly. - call_sign (
str | None): The clan call sign to edit it with. - homepage (
aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): If provided and set to0, The clan chat homepage will be edited toWall. If provided and set to1, The clan chat homepage will be edited toForum. If provided and set to0, The clan chat homepage will be edited toAllianceForum. - enable_invite_messaging_for_admins (
bool | None): ??? - default_publicity (
aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): If provided and set to0, The clan chat publicity will be edited toPublic. If provided and set to1, The clan chat publicity will be edited toAlliance. If provided and set to2, The clan chat publicity will be edited toPrivate. - is_public_topic_admin (
bool | None): ???
1338 async def edit_clan_options( 1339 self, 1340 access_token: str, 1341 /, 1342 group_id: int, 1343 *, 1344 invite_permissions_override: bool | None = None, 1345 update_culture_permissionOverride: bool | None = None, 1346 host_guided_game_permission_override: typing.Literal[0, 1, 2] | None = None, 1347 update_banner_permission_override: bool | None = None, 1348 join_level: enums.ClanMemberType | int | None = None, 1349 ) -> None: 1350 payload = { 1351 "InvitePermissionOverride": invite_permissions_override, 1352 "UpdateCulturePermissionOverride": update_culture_permissionOverride, 1353 "HostGuidedGamePermissionOverride": host_guided_game_permission_override, 1354 "UpdateBannerPermissionOverride": update_banner_permission_override, 1355 "JoinLevel": int(join_level) if join_level else None, 1356 } 1357 1358 await self._request( 1359 _POST, 1360 f"GroupV2/{group_id}/EditFounderOptions", 1361 json=payload, 1362 auth=access_token, 1363 )
Edit the clan options.
Notes
- This request requires OAuth2: oauth2:
AdminGroupsscope. - All arguments will default to
Noneif not provided. This does not includeaccess_tokenandgroup_id
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group id.
Other Parameters
- invite_permissions_override (
bool | None): Minimum Member Level allowed to invite new members to group Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups. - update_culture_permissionOverride (
bool | None): Minimum Member Level allowed to update group culture Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups. - host_guided_game_permission_override (
aiobungie.typedefs.NoneOr[typing.Literal[0, 1, 2]]): Minimum Member Level allowed to host guided games Always Allowed: Founder, Acting Founder, Admin Allowed Overrides:0-> None,1-> Beginner2-> Member. Default is Member for clans, None for groups, although this means nothing for groups. - update_banner_permission_override (
bool | None): Minimum Member Level allowed to update banner Always Allowed: Founder, Acting Founder True means admins have this power, false means they don't Default is False for clans, True for groups. - join_level (
aiobungie.ClanMemberType): Level to join a member at when accepting an invite, application, or joining an open clan. Default isaiobungie.ClanMemberType.BEGINNER
1365 async def fetch_friends(self, access_token: str, /) -> typedefs.JSONObject: 1366 resp = await self._request( 1367 _GET, 1368 "Social/Friends/", 1369 auth=access_token, 1370 ) 1371 assert isinstance(resp, dict) 1372 return resp
Fetch bungie friend list.
This requests OAuth2: ReadUserData scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account.
Returns
aiobungie.typedefs.JSONObject: A JSON object of an array of the bungie friends's data.
1374 async def fetch_friend_requests(self, access_token: str, /) -> typedefs.JSONObject: 1375 resp = await self._request( 1376 _GET, 1377 "Social/Friends/Requests", 1378 auth=access_token, 1379 ) 1380 assert isinstance(resp, dict) 1381 return resp
Fetch pending bungie friend requests queue.
This requests OAuth2: ReadUserData scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account.
Returns
aiobungie.typedefs.JSONObject: A JSON object of incoming requests and outgoing requests.
1383 async def accept_friend_request(self, access_token: str, /, member_id: int) -> None: 1384 await self._request( 1385 _POST, 1386 f"Social/Friends/Requests/Accept/{member_id}", 1387 auth=access_token, 1388 )
Accepts a friend relationship with the target user. The user must be on your incoming friend request list.
This request requires OAuth2: BnetWrite scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - member_id (
int): The member's id to accept.
1390 async def send_friend_request(self, access_token: str, /, member_id: int) -> None: 1391 await self._request( 1392 _POST, 1393 f"Social/Friends/Add/{member_id}", 1394 auth=access_token, 1395 )
Requests a friend relationship with the target user.
This request requires OAuth2: BnetWrite scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - member_id (
int): The member's id to send the request to.
1397 async def decline_friend_request( 1398 self, access_token: str, /, member_id: int 1399 ) -> None: 1400 await self._request( 1401 _POST, 1402 f"Social/Friends/Requests/Decline/{member_id}", 1403 auth=access_token, 1404 )
Decline a friend request with the target user. The user must be in your incoming friend request list.
This request requires OAuth2: BnetWrite scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - member_id (
int): The member's id to decline.
1406 async def remove_friend(self, access_token: str, /, member_id: int) -> None: 1407 await self._request( 1408 _POST, 1409 f"Social/Friends/Remove/{member_id}", 1410 auth=access_token, 1411 )
Removes a friend from your friend list. The user must be in your friend list.
This request requires OAuth2: BnetWrite scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - member_id (
int): The member's id to remove.
1413 async def remove_friend_request(self, access_token: str, /, member_id: int) -> None: 1414 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1415 await self._request( 1416 _POST, 1417 f"Social/Friends/Requests/Remove/{member_id}", 1418 auth=access_token, 1419 )
Removes a friend from your friend list requests. The user must be in your outgoing request list.
.. note : This request requires OAuth2: BnetWrite scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - member_id (
int): The member's id to remove from the requested friend list.
1421 async def approve_all_pending_group_users( 1422 self, 1423 access_token: str, 1424 /, 1425 group_id: int, 1426 message: str | None = None, 1427 ) -> None: 1428 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1429 await self._request( 1430 _POST, 1431 f"GroupV2/{group_id}/Members/ApproveAll", 1432 auth=access_token, 1433 json={"message": str(message)}, 1434 )
Approve all pending users for the given group id.
This request requires OAuth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The given group id.
Other Parameters
- message (
aiobungie.UndefinedOr[str]): An optional message to send with the request. Default isUNDEFINED.
1436 async def deny_all_pending_group_users( 1437 self, 1438 access_token: str, 1439 /, 1440 group_id: int, 1441 *, 1442 message: str | None = None, 1443 ) -> None: 1444 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1445 await self._request( 1446 _POST, 1447 f"GroupV2/{group_id}/Members/DenyAll", 1448 auth=access_token, 1449 json={"message": str(message)}, 1450 )
Deny all pending users for the given group id.
This request requires OAuth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The given group id.
Other Parameters
- message (
aiobungie.UndefinedOr[str]): An optional message to send with the request. Default isUNDEFINED.
1452 async def add_optional_conversation( 1453 self, 1454 access_token: str, 1455 /, 1456 group_id: int, 1457 *, 1458 name: str | None = None, 1459 security: typing.Literal[0, 1] = 0, 1460 ) -> None: 1461 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1462 payload = {"chatName": str(name), "chatSecurity": security} 1463 await self._request( 1464 _POST, 1465 f"GroupV2/{group_id}/OptionalConversations/Add", 1466 json=payload, 1467 auth=access_token, 1468 )
Add a new chat channel to a group.
This request requires OAuth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The given group id.
Other parameters
name: aiobungie.UndefinedOr[str]
The chat name. Default to UNDEFINED
security: typing.Literal[0, 1]
The security level of the chat.
If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
1470 async def edit_optional_conversation( 1471 self, 1472 access_token: str, 1473 /, 1474 group_id: int, 1475 conversation_id: int, 1476 *, 1477 name: str | None = None, 1478 security: typing.Literal[0, 1] = 0, 1479 enable_chat: bool = False, 1480 ) -> None: 1481 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1482 payload = { 1483 "chatEnabled": enable_chat, 1484 "chatName": str(name), 1485 "chatSecurity": security, 1486 } 1487 await self._request( 1488 _POST, 1489 f"GroupV2/{group_id}/OptionalConversations/Edit/{conversation_id}", 1490 json=payload, 1491 auth=access_token, 1492 )
Edit the settings of this chat channel.
This request requires OAuth2: AdminGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The given group id. - conversation_id (
int): The conversation/chat id.
Other parameters
name: aiobungie.UndefinedOr[str]
The new chat name. Default to UNDEFINED
security: typing.Literal[0, 1]
The new security level of the chat.
If provided and set to 0, It will be to `Group` only.
If provided and set to 1, It will be `Admins` only.
Default is `0`
enable_chat : bool
Whether to enable chatting or not.
If set to True then chatting will be enabled. Otherwise it will be disabled.
1494 async def transfer_item( 1495 self, 1496 access_token: str, 1497 /, 1498 item_id: int, 1499 item_hash: int, 1500 character_id: int, 1501 member_type: enums.MembershipType | int, 1502 *, 1503 stack_size: int = 1, 1504 vault: bool = False, 1505 ) -> None: 1506 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1507 payload = { 1508 "characterId": character_id, 1509 "membershipType": int(member_type), 1510 "itemId": item_id, 1511 "itemReferenceHash": item_hash, 1512 "stackSize": stack_size, 1513 "transferToVault": vault, 1514 } 1515 await self._request( 1516 _POST, 1517 "Destiny2/Actions/Items/TransferItem", 1518 json=payload, 1519 auth=access_token, 1520 )
Transfer an item from / to your vault.
Notes
- This method requires OAuth2: MoveEquipDestinyItems scope.
- This method requires both item id and hash.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - item_id (
int): The item instance id you to transfer. - item_hash (
int): The item hash. - character_id (
int): The character id to transfer the item from/to. - member_type (
aiobungie.aiobungie.MembershipType | int): The user membership type.
Other Parameters
- stack_size (
int): The item stack size. - vault (
bool): Whether to transfer this item to your vault or not. Defaults toFalse.
1522 async def pull_item( 1523 self, 1524 access_token: str, 1525 /, 1526 item_id: int, 1527 item_hash: int, 1528 character_id: int, 1529 member_type: enums.MembershipType | int, 1530 *, 1531 stack_size: int = 1, 1532 vault: bool = False, 1533 ) -> None: 1534 # <<inherited docstring from aiobungie.interfaces.rest.RESTInterface>> 1535 payload = { 1536 "characterId": character_id, 1537 "membershipType": int(member_type), 1538 "itemId": item_id, 1539 "itemReferenceHash": item_hash, 1540 "stackSize": stack_size, 1541 "transferToVault": vault, 1542 } 1543 await self._request( 1544 _POST, 1545 "Destiny2/Actions/Items/PullFromPostmaster", 1546 json=payload, 1547 auth=access_token, 1548 )
pull an item from the postmaster.
Notes
- This method requires OAuth2: MoveEquipDestinyItems scope.
- This method requires both item id and hash.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - item_id (
int): The item instance id to pull. - item_hash (
int): The item hash. - character_id (
int): The character id to pull the item to. - member_type (
aiobungie.aiobungie.MembershipType | int): The user membership type.
Other Parameters
- stack_size (
int): The item stack size. - vault (
bool): Whether to pill this item to your vault or not. Defaults toFalse.
1550 async def fetch_fireteams( 1551 self, 1552 activity_type: fireteams.FireteamActivity | int, 1553 *, 1554 platform: fireteams.FireteamPlatform | int = fireteams.FireteamPlatform.ANY, 1555 language: fireteams.FireteamLanguage | str = fireteams.FireteamLanguage.ALL, 1556 date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL, 1557 page: int = 0, 1558 slots_filter: int = 0, 1559 ) -> typedefs.JSONObject: 1560 resp = await self._request( 1561 _GET, 1562 f"Fireteam/Search/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{page}/?langFilter={str(language)}", # noqa: E501 Line too long 1563 ) 1564 assert isinstance(resp, dict) 1565 return resp
Fetch public Bungie fireteams with open slots.
Parameters
- activity_type (
aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
- platform (
aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults toaiobungie.crates.FireteamPlatform.ANYwhich returns all platforms. - language (
FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults toaiobungie.crates.FireteamLanguage.ALL - date_range (
aiobungie.aiobungie.FireteamDate | int): An integer to filter the date range of the returned fireteams. Defaults toaiobungie.FireteamDate.ALL. - page (
int): The page number. By default its0which returns all available activities. - slots_filter (
int): Filter the returned fireteams based on available slots. Default is0
Returns
aiobungie.typedefs.JSONObject: A JSON object of the fireteam details.
1567 async def fetch_available_clan_fireteams( 1568 self, 1569 access_token: str, 1570 group_id: int, 1571 activity_type: fireteams.FireteamActivity | int, 1572 *, 1573 platform: fireteams.FireteamPlatform | int, 1574 language: fireteams.FireteamLanguage | str, 1575 date_range: fireteams.FireteamDate | int = fireteams.FireteamDate.ALL, 1576 page: int = 0, 1577 public_only: bool = False, 1578 slots_filter: int = 0, 1579 ) -> typedefs.JSONObject: 1580 resp = await self._request( 1581 _GET, 1582 f"Fireteam/Clan/{group_id}/Available/{int(platform)}/{int(activity_type)}/{int(date_range)}/{slots_filter}/{public_only}/{page}", # noqa: E501 1583 json={"langFilter": str(language)}, 1584 auth=access_token, 1585 ) 1586 assert isinstance(resp, dict) 1587 return resp
Fetch a clan's fireteams with open slots.
This method requires OAuth2: ReadGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group/clan id of the fireteam. - activity_type (
aiobungie.aiobungie.crates.FireteamActivity | int): The fireteam activity type.
Other Parameters
- platform (
aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults toaiobungie.crates.FireteamPlatform.ANYwhich returns all platforms. - language (
FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults toaiobungie.crates.FireteamLanguage.ALL - date_range (
aiobungie.aiobungie.FireteamDate | int): An integer to filter the date range of the returned fireteams. Defaults toaiobungie.FireteamDate.ALL. - page (
int): The page number. By default its0which returns all available activities. - public_only (
bool): If set to True, Then only public fireteams will be returned. - slots_filter (
int): Filter the returned fireteams based on available slots. Default is0
Returns
aiobungie.typedefs.JSONObject: A JSON object of the fireteams detail.
1589 async def fetch_clan_fireteam( 1590 self, access_token: str, fireteam_id: int, group_id: int 1591 ) -> typedefs.JSONObject: 1592 resp = await self._request( 1593 _GET, 1594 f"Fireteam/Clan/{group_id}/Summary/{fireteam_id}", 1595 auth=access_token, 1596 ) 1597 assert isinstance(resp, dict) 1598 return resp
Fetch a specific clan fireteam.
This method requires OAuth2: ReadGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group/clan id to fetch the fireteam from. - fireteam_id (
int): The fireteam id to fetch.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the fireteam details.
1600 async def fetch_my_clan_fireteams( 1601 self, 1602 access_token: str, 1603 group_id: int, 1604 *, 1605 include_closed: bool = True, 1606 platform: fireteams.FireteamPlatform | int, 1607 language: fireteams.FireteamLanguage | str, 1608 filtered: bool = True, 1609 page: int = 0, 1610 ) -> typedefs.JSONObject: 1611 payload = {"groupFilter": filtered, "langFilter": str(language)} 1612 1613 resp = await self._request( 1614 _GET, 1615 f"Fireteam/Clan/{group_id}/My/{int(platform)}/{include_closed}/{page}", 1616 json=payload, 1617 auth=access_token, 1618 ) 1619 assert isinstance(resp, dict) 1620 return resp
Fetch a clan's fireteams with open slots.
This method requires OAuth2: ReadGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group/clan id to fetch.
Other Parameters
- include_closed (
bool): If provided and set toTrue, It will also return closed fireteams. If provided and set toFalse, It will only return public fireteams. Default isTrue. - platform (
aiobungie.aiobungie.crates.fireteams.FireteamPlatform | int): If this is provided. Then the results will be filtered with the given platform. Defaults toaiobungie.crates.FireteamPlatform.ANYwhich returns all platforms. - language (
FireteamLanguage | str): A locale language to filter the used language in that fireteam. Defaults toaiobungie.crates.FireteamLanguage.ALL - filtered (
bool): If set toTrue, it will filter by clan. Otherwise not. Default isTrue. - page (
int): The page number. By default its0which returns all available activities.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the fireteams detail.
1622 async def fetch_private_clan_fireteams( 1623 self, access_token: str, group_id: int, / 1624 ) -> int: 1625 resp = await self._request( 1626 _GET, 1627 f"Fireteam/Clan/{group_id}/ActiveCount", 1628 auth=access_token, 1629 ) 1630 assert isinstance(resp, int) 1631 return resp
Fetch the active count of the clan fireteams that are only private.
This method requires OAuth2: ReadGroups scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - group_id (
int): The group/clan id.
Returns
int: The active fireteams count. Max value returned is 25.
1633 async def fetch_post_activity(self, instance_id: int, /) -> typedefs.JSONObject: 1634 resp = await self._request( 1635 _GET, f"Destiny2/Stats/PostGameCarnageReport/{instance_id}" 1636 ) 1637 assert isinstance(resp, dict) 1638 return resp
Fetch a post activity details.
Parameters
- instance_id (
int): The activity instance id.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the post activity.
1640 async def search_entities( 1641 self, name: str, entity_type: str, *, page: int = 0 1642 ) -> typedefs.JSONObject: 1643 resp = await self._request( 1644 _GET, 1645 f"Destiny2/Armory/Search/{entity_type}/{name}/", 1646 json={"page": page}, 1647 ) 1648 assert isinstance(resp, dict) 1649 return resp
Search for Destiny2 entities given a name and its type.
Parameters
- name (
str): The name of the entity, i.e., Thunderlord, One thousand voices. - entity_type (
str): The type of the entity, AKA Definition, For an exampleDestinyInventoryItemDefinition
Other Parameters
- page (
int): An optional page to return. Default to 0.
Returns
aiobungie.typedefs.JSONObject: A JSON object contains details about the searched term.
1651 async def fetch_unique_weapon_history( 1652 self, 1653 membership_id: int, 1654 character_id: int, 1655 membership_type: enums.MembershipType | int, 1656 ) -> typedefs.JSONObject: 1657 resp = await self._request( 1658 _GET, 1659 f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/UniqueWeapons/", 1660 ) 1661 assert isinstance(resp, dict) 1662 return resp
Fetch details about unique weapon usage for a character. Includes all exotics.
Parameters
- membership_id (
int): The Destiny user membership id. - character_id (
int): The character id to retrieve. - membership_type (
aiobungie.aiobungie.MembershipType | int): The Destiny user's membership type.
Returns
aiobungie.typedefs.JSONObject: A JSON object contains details about the returned weapons.
1664 async def fetch_item( 1665 self, 1666 member_id: int, 1667 item_id: int, 1668 membership_type: enums.MembershipType | int, 1669 components: collections.Sequence[enums.ComponentType], 1670 ) -> typedefs.JSONObject: 1671 collector = _collect_components(components) 1672 1673 resp = await self._request( 1674 _GET, 1675 f"Destiny2/{int(membership_type)}/Profile/{member_id}/Item/{item_id}/?components={collector}", 1676 ) 1677 assert isinstance(resp, dict) 1678 return resp
Fetch an instanced Destiny 2 item's details.
Parameters
- member_id (
int): The membership id of the Destiny 2 player. - item_id (
int): The instance id of the item. - membership_type (
aiobungie.aiobungie.MembershipType | int): The membership type of the Destiny 2 player. - components (
collections.Sequence[aiobungie.ComponentType]): A list of components to retrieve.
Returns
aiobungie.typedefs.JSONObject: A JSON object response contains the fetched item with its components.
1680 async def fetch_clan_weekly_rewards(self, clan_id: int, /) -> typedefs.JSONObject: 1681 resp = await self._request(_GET, f"Destiny2/Clan/{clan_id}/WeeklyRewardState/") 1682 assert isinstance(resp, dict) 1683 return resp
Fetch the weekly reward state for a clan.
Parameters
- clan_id (
int): The clan id.
Returns
aiobungie.typedefs.JSONObject: A JSON response of the clan rewards state.
1685 async def fetch_available_locales(self) -> typedefs.JSONObject: 1686 resp = await self._request(_GET, "Destiny2/Manifest/DestinyLocaleDefinition/") 1687 assert isinstance(resp, dict) 1688 return resp
Fetch available locales at Bungie.
Returns
aiobungie.typedefs.JSONObject: A JSON object contains a list of all available localization cultures.
1690 async def fetch_common_settings(self) -> typedefs.JSONObject: 1691 resp = await self._request(_GET, "Settings") 1692 assert isinstance(resp, dict) 1693 return resp
Fetch the common settings used by Bungie's environment.
Returns
aiobungie.typedefs.JSONObject: The common settings JSON object.
1695 async def fetch_user_systems_overrides(self) -> typedefs.JSONObject: 1696 resp = await self._request(_GET, "UserSystemOverrides") 1697 assert isinstance(resp, dict) 1698 return resp
Fetch a user's specific system overrides.
Returns
aiobungie.typedefs.JSONObject: The system overrides JSON object.
1700 async def fetch_global_alerts( 1701 self, *, include_streaming: bool = False 1702 ) -> typedefs.JSONArray: 1703 resp = await self._request( 1704 _GET, f"GlobalAlerts/?includestreaming={include_streaming}" 1705 ) 1706 assert isinstance(resp, list) 1707 return resp
Fetch any active global alerts.
Parameters
- include_streaming (
bool): If True, the returned results will include streaming alerts. Default is False.
Returns
aiobungie.typedefs.JSONArray: A JSON array of the global alerts objects.
1709 async def awainitialize_request( 1710 self, 1711 access_token: str, 1712 type: typing.Literal[0, 1], 1713 membership_type: enums.MembershipType | int, 1714 /, 1715 *, 1716 affected_item_id: int | None = None, 1717 character_id: int | None = None, 1718 ) -> typedefs.JSONObject: 1719 body = {"type": type, "membershipType": int(membership_type)} 1720 1721 if affected_item_id is not None: 1722 body["affectedItemId"] = affected_item_id 1723 1724 if character_id is not None: 1725 body["characterId"] = character_id 1726 1727 resp = await self._request( 1728 _POST, "Destiny2/Awa/Initialize", json=body, auth=access_token 1729 ) 1730 assert isinstance(resp, dict) 1731 return resp
Initialize a request to perform an advanced write action.
OAuth2: AdvancedWriteActions application scope is required to perform this request.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - type (
typing.Literal[0, 1]): Type of the advanced write action. Its either 0 or 1. If set to 0 that means itNone. Otherwise if 1 that means its insert plugs. - membership_type (
aiobungie.aiobungie.MembershipType | int): The Destiny membership type of the account to modify.
Other Parameters
- affected_item_id (
int | None): Item instance ID the action shall be applied to. This is optional for all but a new AwaType values. - character_id (
int | None): The Destiny character ID to perform this action on.
Returns
aiobungie.typedefs.JSONObject: A JSON object response.
1733 async def awaget_action_token( 1734 self, access_token: str, correlation_id: str, / 1735 ) -> typedefs.JSONObject: 1736 resp = await self._request( 1737 _POST, 1738 f"Destiny2/Awa/GetActionToken/{correlation_id}", 1739 auth=access_token, 1740 ) 1741 assert isinstance(resp, dict) 1742 return resp
Returns the action token if user approves the request.
OAuth2: AdvancedWriteActions application scope is required to perform this request.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - correlation_id (
str): The identifier for the advanced write action request.
Returns
aiobungie.typedefs.JSONObject: A JSON object response.
1762 async def fetch_vendors( 1763 self, 1764 access_token: str, 1765 character_id: int, 1766 membership_id: int, 1767 membership_type: enums.MembershipType | int, 1768 /, 1769 components: collections.Sequence[enums.ComponentType], 1770 filter: int | None = None, 1771 ) -> typedefs.JSONObject: 1772 components_ = _collect_components(components) 1773 route = ( 1774 f"Destiny2/{int(membership_type)}/Profile/{membership_id}" 1775 f"/Character/{character_id}/Vendors/?components={components_}" 1776 ) 1777 1778 if filter is not None: 1779 route = route + f"&filter={filter}" 1780 1781 resp = await self._request( 1782 _GET, 1783 route, 1784 auth=access_token, 1785 ) 1786 assert isinstance(resp, dict) 1787 return resp
Get currently available vendors from the list of vendors that can possibly have rotating inventory.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - character_id (
int): The character ID to return the vendor info for. - membership_id (
int): The Destiny membership id to return the vendor info for. - membership_type (
aiobungie.aiobungie.MembershipType | int): The Destiny membership type to return the vendor info for. - components (
collections.Sequence[aiobungie.ComponentType]): A list of vendor components to collect and return.
Other Parameters
- filter (
int): Filters the type of items returned from the vendor. This can be left toNone.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the vendor response.
1789 async def fetch_vendor( 1790 self, 1791 access_token: str, 1792 character_id: int, 1793 membership_id: int, 1794 membership_type: enums.MembershipType | int, 1795 vendor_hash: int, 1796 /, 1797 components: collections.Sequence[enums.ComponentType], 1798 ) -> typedefs.JSONObject: 1799 components_ = _collect_components(components) 1800 resp = await self._request( 1801 _GET, 1802 ( 1803 f"Destiny2/{int(membership_type)}/Profile/{membership_id}" 1804 f"/Character/{character_id}/Vendors/{vendor_hash}/?components={components_}" 1805 ), 1806 auth=access_token, 1807 ) 1808 assert isinstance(resp, dict) 1809 return resp
Fetch details for a specific vendor.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - character_id (
int): The character ID to return the vendor info for. - membership_id (
int): The Destiny membership id to return the vendor info for. - membership_type (
aiobungie.aiobungie.MembershipType | int): The Destiny membership type to return the vendor info for. - vendor_hash (
int): The vendor hash to return the details for. - components (
collections.Sequence[aiobungie.ComponentType]): A list of vendor components to collect and return.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the vendor response.
1811 async def fetch_application_api_usage( 1812 self, 1813 access_token: str, 1814 application_id: int, 1815 /, 1816 *, 1817 start: datetime.datetime | None = None, 1818 end: datetime.datetime | None = None, 1819 ) -> typedefs.JSONObject: 1820 end_date, start_date = time.parse_date_range(end, start) 1821 resp = await self._request( 1822 _GET, 1823 f"App/ApiUsage/{application_id}/?end={end_date}&start={start_date}", 1824 auth=access_token, 1825 ) 1826 assert isinstance(resp, dict) 1827 return resp
Fetch a Bungie application's API usage.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - application_id (
int): The application id to get.
Other Parameters
start (
datetime.datetime | None): A datetime object can be used to collect the start of the application usage. This is limited and can go back to 30 days maximum.If this is left to
None. It will return the last 24 hours.end (
datetime.datetime | None): A datetime object can be used to collect the end of the application usage.If this is left to
None. It will returnnow.
Example
import datetime
# Fetch data from 2021 Dec 10th to 2021 Dec 20th
await fetch_application_api_usage(
start=datetime.datetime(2021, 12, 10),
end=datetime.datetime(2021, 12, 20)
)
Returns
aiobungie.typedefs.JSONObject: A JSON object of the application usage details.
1829 async def fetch_bungie_applications(self) -> typedefs.JSONArray: 1830 resp = await self._request(_GET, "App/FirstParty") 1831 assert isinstance(resp, list) 1832 return resp
Fetch details for applications created by Bungie.
Returns
aiobungie.typedefs.JSONArray: An array of Bungie created applications.
1839 async def fetch_content_by_id( 1840 self, id: int, locale: str, /, *, head: bool = False 1841 ) -> typedefs.JSONObject: 1842 resp = await self._request( 1843 _GET, 1844 f"Content/GetContentById/{id}/{locale}/", 1845 json={"head": head}, 1846 ) 1847 assert isinstance(resp, dict) 1848 return resp
1850 async def fetch_content_by_tag_and_type( 1851 self, locale: str, tag: str, type: str, *, head: bool = False 1852 ) -> typedefs.JSONObject: 1853 resp = await self._request( 1854 _GET, 1855 f"Content/GetContentByTagAndType/{tag}/{type}/{locale}/", 1856 json={"head": head}, 1857 ) 1858 assert isinstance(resp, dict) 1859 return resp
1861 async def search_content_with_text( 1862 self, 1863 locale: str, 1864 /, 1865 content_type: str, 1866 search_text: str, 1867 tag: str, 1868 *, 1869 page: int | None = None, 1870 source: str | None = None, 1871 ) -> typedefs.JSONObject: 1872 body: typedefs.JSONObject = { 1873 "locale": locale, 1874 "currentpage": page or 1, 1875 "ctype": content_type, 1876 "searchtxt": search_text, 1877 "searchtext": search_text, 1878 "tag": tag, 1879 "source": source, 1880 } 1881 1882 resp = await self._request(_GET, "Content/Search", params=body) 1883 assert isinstance(resp, dict) 1884 return resp
1886 async def search_content_by_tag_and_type( 1887 self, 1888 locale: str, 1889 tag: str, 1890 type: str, 1891 *, 1892 page: int | None = None, 1893 ) -> typedefs.JSONObject: 1894 body: typedefs.JSONObject = {"currentpage": page or 1} 1895 1896 resp = await self._request( 1897 _GET, 1898 f"Content/SearchContentByTagAndType/{tag}/{type}/{locale}/", 1899 params=body, 1900 ) 1901 assert isinstance(resp, dict) 1902 return resp
1911 async def fetch_topics_page( 1912 self, 1913 category_filter: int, 1914 group: int, 1915 date_filter: int, 1916 sort: str | bytes, 1917 *, 1918 page: int | None = None, 1919 locales: collections.Iterable[str] | None = None, 1920 tag_filter: str | None = None, 1921 ) -> typedefs.JSONObject: 1922 params = { 1923 "locales": ",".join(locales) if locales is not None else "en", 1924 } 1925 if tag_filter: 1926 params["tagstring"] = tag_filter 1927 1928 resp = await self._request( 1929 _GET, 1930 f"Forum/GetTopicsPaged/{page or 0}/0/{group}/{sort!s}/{date_filter}/{category_filter}/", 1931 params=params, 1932 ) 1933 assert isinstance(resp, dict) 1934 return resp
1936 async def fetch_core_topics_page( 1937 self, 1938 category_filter: int, 1939 date_filter: int, 1940 sort: str | bytes, 1941 *, 1942 page: int | None = None, 1943 locales: collections.Iterable[str] | None = None, 1944 ) -> typedefs.JSONObject: 1945 resp = await self._request( 1946 _GET, 1947 f"Forum/GetCoreTopicsPaged/{page or 0}" 1948 f"/{sort!s}/{date_filter}/{category_filter}/?locales={','.join(locales) if locales else 'en'}", 1949 ) 1950 assert isinstance(resp, dict) 1951 return resp
1953 async def fetch_posts_threaded_page( 1954 self, 1955 parent_post: bool, 1956 page: int, 1957 page_size: int, 1958 parent_post_id: int, 1959 reply_size: int, 1960 root_thread_mode: bool, 1961 sort_mode: int, 1962 show_banned: str | None = None, 1963 ) -> typedefs.JSONObject: 1964 resp = await self._request( 1965 _GET, 1966 f"Forum/GetPostsThreadedPaged/{parent_post}/{page}/" 1967 f"{page_size}/{reply_size}/{parent_post_id}/{root_thread_mode}/{sort_mode}/", 1968 json={"showbanned": show_banned}, 1969 ) 1970 assert isinstance(resp, dict) 1971 return resp
1973 async def fetch_posts_threaded_page_from_child( 1974 self, 1975 child_id: bool, 1976 page: int, 1977 page_size: int, 1978 reply_size: int, 1979 root_thread_mode: bool, 1980 sort_mode: int, 1981 show_banned: str | None = None, 1982 ) -> typedefs.JSONObject: 1983 resp = await self._request( 1984 _GET, 1985 f"Forum/GetPostsThreadedPagedFromChild/{child_id}/" 1986 f"{page}/{page_size}/{reply_size}/{root_thread_mode}/{sort_mode}/", 1987 json={"showbanned": show_banned}, 1988 ) 1989 assert isinstance(resp, dict) 1990 return resp
1992 async def fetch_post_and_parent( 1993 self, child_id: int, /, *, show_banned: str | None = None 1994 ) -> typedefs.JSONObject: 1995 resp = await self._request( 1996 _GET, 1997 f"Forum/GetPostAndParent/{child_id}/", 1998 json={"showbanned": show_banned}, 1999 ) 2000 assert isinstance(resp, dict) 2001 return resp
2003 async def fetch_posts_and_parent_awaiting( 2004 self, child_id: int, /, *, show_banned: str | None = None 2005 ) -> typedefs.JSONObject: 2006 resp = await self._request( 2007 _GET, 2008 f"Forum/GetPostAndParentAwaitingApproval/{child_id}/", 2009 json={"showbanned": show_banned}, 2010 ) 2011 assert isinstance(resp, dict) 2012 return resp
2040 async def fetch_recommended_groups( 2041 self, 2042 access_token: str, 2043 /, 2044 *, 2045 date_range: int = 0, 2046 group_type: enums.GroupType | int = enums.GroupType.CLAN, 2047 ) -> typedefs.JSONArray: 2048 resp = await self._request( 2049 _POST, 2050 f"GroupV2/Recommended/{int(group_type)}/{date_range}/", 2051 auth=access_token, 2052 ) 2053 assert isinstance(resp, list) 2054 return resp
2061 async def fetch_user_clan_invite_setting( 2062 self, 2063 access_token: str, 2064 /, 2065 membership_type: enums.MembershipType | int, 2066 ) -> bool: 2067 resp = await self._request( 2068 _GET, 2069 f"GroupV2/GetUserClanInviteSetting/{int(membership_type)}/", 2070 auth=access_token, 2071 ) 2072 assert isinstance(resp, bool) 2073 return resp
2075 async def fetch_banned_group_members( 2076 self, access_token: str, group_id: int, /, *, page: int = 1 2077 ) -> typedefs.JSONObject: 2078 resp = await self._request( 2079 _GET, 2080 f"GroupV2/{group_id}/Banned/?currentpage={page}", 2081 auth=access_token, 2082 ) 2083 assert isinstance(resp, dict) 2084 return resp
2086 async def fetch_pending_group_memberships( 2087 self, access_token: str, group_id: int, /, *, current_page: int = 1 2088 ) -> typedefs.JSONObject: 2089 resp = await self._request( 2090 _GET, 2091 f"GroupV2/{group_id}/Members/Pending/?currentpage={current_page}", 2092 auth=access_token, 2093 ) 2094 assert isinstance(resp, dict) 2095 return resp
2097 async def fetch_invited_group_memberships( 2098 self, access_token: str, group_id: int, /, *, current_page: int = 1 2099 ) -> typedefs.JSONObject: 2100 resp = await self._request( 2101 _GET, 2102 f"GroupV2/{group_id}/Members/InvitedIndividuals/?currentpage={current_page}", 2103 auth=access_token, 2104 ) 2105 assert isinstance(resp, dict) 2106 return resp
2108 async def invite_member_to_group( 2109 self, 2110 access_token: str, 2111 /, 2112 group_id: int, 2113 membership_id: int, 2114 membership_type: enums.MembershipType | int, 2115 *, 2116 message: str | None = None, 2117 ) -> typedefs.JSONObject: 2118 resp = await self._request( 2119 _POST, 2120 f"GroupV2/{group_id}/Members/IndividualInvite/{int(membership_type)}/{membership_id}/", 2121 auth=access_token, 2122 json={"message": str(message)}, 2123 ) 2124 assert isinstance(resp, dict) 2125 return resp
2127 async def cancel_group_member_invite( 2128 self, 2129 access_token: str, 2130 /, 2131 group_id: int, 2132 membership_id: int, 2133 membership_type: enums.MembershipType | int, 2134 ) -> typedefs.JSONObject: 2135 resp = await self._request( 2136 _POST, 2137 f"GroupV2/{group_id}/Members/IndividualInviteCancel/{int(membership_type)}/{membership_id}/", 2138 auth=access_token, 2139 ) 2140 assert isinstance(resp, dict) 2141 return resp
2148 async def fetch_historical_stats( 2149 self, 2150 character_id: int, 2151 membership_id: int, 2152 membership_type: enums.MembershipType | int, 2153 day_start: datetime.datetime, 2154 day_end: datetime.datetime, 2155 groups: collections.Sequence[enums.StatsGroupType | int], 2156 modes: collections.Sequence[enums.GameMode | int], 2157 *, 2158 period_type: enums.PeriodType = enums.PeriodType.ALL_TIME, 2159 ) -> typedefs.JSONObject: 2160 end, start = time.parse_date_range(day_end, day_start) 2161 resp = await self._request( 2162 _GET, 2163 f"Destiny2/{int(membership_type)}/Account/{membership_id}/Character/{character_id}/Stats/", 2164 json={ 2165 "dayend": end, 2166 "daystart": start, 2167 "groups": [str(int(group)) for group in groups], 2168 "modes": [str(int(mode)) for mode in modes], 2169 "periodType": int(period_type), 2170 }, 2171 ) 2172 assert isinstance(resp, dict) 2173 return resp
Fetch historical stats for a specific membership character.
Parameters
- character_id (
int): The character ID to return the stats for. - membership_id (
int): The Destiny membership id to return the stats for. - membership_type (
aiobungie.MembershipType | int): The Destiny membership type to return the stats for. - day_start (
datetime.datetime): The start of the day to return the stats for. - day_end (
datetime.datetime): The end of the day to return the stats for. - groups (
collections.Sequence[aiobungie.StatsGroupType]): A list of stats groups to return. - modes (
collections.Sequence[aiobungie.GameMode | int]): A list of game modes to return. - period_type (
aiobungie.enums.PeriodType): The period type to return the stats for. This will returnALL_TIMEby default if not modified.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the historical stats.
2175 async def fetch_historical_stats_for_account( 2176 self, 2177 membership_id: int, 2178 membership_type: enums.MembershipType | int, 2179 groups: collections.Sequence[enums.StatsGroupType | int], 2180 ) -> typedefs.JSONObject: 2181 resp = await self._request( 2182 _GET, 2183 f"Destiny2/{int(membership_type)}/Account/{membership_id}/Stats/", 2184 json={"groups": [str(int(group)) for group in groups]}, 2185 ) 2186 assert isinstance(resp, dict) 2187 return resp
Fetch historical stats for an account's membership.
Parameters
- membership_id (
int): The Destiny membership id to return the stats for. - membership_type (
aiobungie.MembershipType | int): The Destiny membership type to return the stats for. - groups (
collections.Sequence[aiobungie.StatsGroupType]): A list of stats groups to return.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the historical stats for the account. This includes both the character and account stats.
2189 async def fetch_aggregated_activity_stats( 2190 self, 2191 character_id: int, 2192 membership_id: int, 2193 membership_type: enums.MembershipType | int, 2194 /, 2195 ) -> typedefs.JSONObject: 2196 resp = await self._request( 2197 _GET, 2198 f"Destiny2/{int(membership_type)}/Account/{membership_id}/" 2199 f"Character/{character_id}/Stats/AggregateActivityStats/", 2200 ) 2201 assert isinstance(resp, dict) 2202 return resp
Fetch aggregated activity stats for a specific membership character.
Parameters
- character_id (
int): The character ID to return the stats for. - membership_id (
int): The Destiny membership id to return the stats for. - membership_type (
aiobungie.MembershipType | int): The Destiny membership type to return the stats for.
Returns
aiobungie.typedefs.JSONObject: A JSON object of the aggregated activity stats.
2204 async def equip_loadout( 2205 self, 2206 access_token: str, 2207 /, 2208 loadout_index: int, 2209 character_id: int, 2210 membership_type: enums.MembershipType | int, 2211 ) -> None: 2212 response = await self._request( 2213 _POST, 2214 "Destiny2/Actions/Loadouts/EquipLoadout/", 2215 json={ 2216 "loadoutIndex": loadout_index, 2217 "characterId": character_id, 2218 "membership_type": int(membership_type), 2219 }, 2220 auth=access_token, 2221 ) 2222 assert isinstance(response, int)
Equip a loadout. Your character must be in a Social space, Orbit or Offline while performing this operation.
This operation requires MoveEquipDestinyItems OAuth2 scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - loadout_index (
int): The index of the loadout to use. - character_id (
int): The character ID to equip the loadout to. - membership_type (
aiobungie.MembershipType | int): The membership type of the account.
2224 async def snapshot_loadout( 2225 self, 2226 access_token: str, 2227 /, 2228 loadout_index: int, 2229 character_id: int, 2230 membership_type: enums.MembershipType | int, 2231 *, 2232 color_hash: int | None = None, 2233 icon_hash: int | None = None, 2234 name_hash: int | None = None, 2235 ) -> None: 2236 response = await self._request( 2237 _POST, 2238 "Destiny2/Actions/Loadouts/SnapshotLoadout/", 2239 auth=access_token, 2240 json={ 2241 "colorHash": color_hash, 2242 "iconHash": icon_hash, 2243 "nameHash": name_hash, 2244 "loadoutIndex": loadout_index, 2245 "characterId": character_id, 2246 "membershipType": int(membership_type), 2247 }, 2248 ) 2249 assert isinstance(response, int)
Snapshot a loadout with the currently equipped items.
This operation requires MoveEquipDestinyItems OAuth2 scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - loadout_index (
int): The index of the loadout to use. - character_id (
int): The character ID to equip the loadout to. - membership_type (
aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
- color_hash (
int | None): ... - icon_hash (
int | None): ... - name_hash (
int | None): ...
2251 async def update_loadout( 2252 self, 2253 access_token: str, 2254 /, 2255 loadout_index: int, 2256 character_id: int, 2257 membership_type: enums.MembershipType | int, 2258 *, 2259 color_hash: int | None = None, 2260 icon_hash: int | None = None, 2261 name_hash: int | None = None, 2262 ) -> None: 2263 response = await self._request( 2264 _POST, 2265 "Destiny2/Actions/Loadouts/UpdateLoadoutIdentifiers/", 2266 auth=access_token, 2267 json={ 2268 "colorHash": color_hash, 2269 "iconHash": icon_hash, 2270 "nameHash": name_hash, 2271 "loadoutIndex": loadout_index, 2272 "characterId": character_id, 2273 "membershipType": int(membership_type), 2274 }, 2275 ) 2276 assert isinstance(response, int)
Update the loadout. Color, Icon and Name.
This operation requires MoveEquipDestinyItems OAuth2 scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - loadout_index (
int): The index of the loadout to use. - character_id (
int): The character ID to equip the loadout to. - membership_type (
aiobungie.MembershipType | int): The membership type of the account.
Other Parameters
- color_hash (
int | None): The new color hash of the loadout to update. - icon_hash (
int | None): The new icon hash of the loadout to update. - name_hash (
int | None): The new name hash of the loadout to update.
2278 async def clear_loadout( 2279 self, 2280 access_token: str, 2281 /, 2282 loadout_index: int, 2283 character_id: int, 2284 membership_type: enums.MembershipType | int, 2285 ) -> None: 2286 response = await self._request( 2287 _POST, 2288 "Destiny2/Actions/Loadouts/ClearLoadout/", 2289 json={ 2290 "loadoutIndex": loadout_index, 2291 "characterId": character_id, 2292 "membership_type": int(membership_type), 2293 }, 2294 auth=access_token, 2295 ) 2296 assert isinstance(response, int)
Clear the identifiers and items of a loadout.
This operation requires MoveEquipDestinyItems OAuth2 scope.
Parameters
- access_token (
str): The bearer access token associated with the bungie account. - loadout_index (
int): The index of the loadout to use. - character_id (
int): The character ID to equip the loadout to. - membership_type (
aiobungie.MembershipType | int): The membership type of the account.
192class RESTPool: 193 """a Pool of `RESTClient` instances. 194 195 This allows to acquire multiple instances of `RESTClient`s which can be acquired with the same token and metadata. 196 197 A full example of this client can be found in the examples directory. 198 199 Example 200 ------- 201 ```py 202 import aiobungie 203 import asyncio 204 205 pool = aiobungie.RESTPool("token") 206 pool.metadata['auth_code'] = 'code' 207 208 async def get() -> None: 209 async with pool.acquire() as rest: 210 this = await rest.fetch_current_user_memberships(pool.metadata['auth_code']) 211 212 await asyncio.run(get()) 213 ``` 214 215 Parameters 216 ---------- 217 token : `str` 218 A valid application token from Bungie's developer portal. 219 220 Other Parameters 221 ---------------- 222 max_retries : `int` 223 The max retries number to retry if the request hit a `5xx` status code. 224 client_secret : `str | None` 225 An optional application client secret, 226 This is only needed if you're fetching OAuth2 tokens with this client. 227 client_id : `int | None` 228 An optional application client id, 229 This is only needed if you're fetching OAuth2 tokens with this client. 230 debug : `bool | str` 231 Whether to enable logging responses or not. 232 233 Logging Levels 234 -------------- 235 * `False`: This will disable logging. 236 * `True`: This will set the level to `DEBUG` and enable logging minimal information. 237 Like the response status, route, taken time and so on. 238 * `"TRACE" | aiobungie.TRACE`: This will log the response headers along with the minimal information. 239 """ 240 241 __slots__ = ( 242 "_token", 243 "_max_retries", 244 "_client_secret", 245 "_client_id", 246 "_metadata", 247 "_enable_debug", 248 "_client_session", 249 "_loads", 250 "_dumps", 251 ) 252 253 # Looks like mypy doesn't like this. 254 if typing.TYPE_CHECKING: 255 _enable_debug: typing.Literal["TRACE"] | bool | int 256 257 def __init__( 258 self, 259 token: str, 260 /, 261 *, 262 client_secret: str | None = None, 263 client_id: int | None = None, 264 client_session: aiohttp.ClientSession | None = None, 265 dumps: typedefs.Dumps = helpers.dumps, 266 loads: typedefs.Loads = helpers.loads, 267 max_retries: int = 4, 268 debug: typing.Literal["TRACE"] | bool | int = False, 269 ) -> None: 270 self._client_secret = client_secret 271 self._client_id = client_id 272 self._token = token 273 self._max_retries = max_retries 274 self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {} 275 self._enable_debug = debug 276 self._client_session = client_session 277 self._loads = loads 278 self._dumps = dumps 279 280 @property 281 def client_id(self) -> int | None: 282 return self._client_id 283 284 @property 285 def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: 286 """Pool's Metadata. This is different from client instance metadata.""" 287 return self._metadata 288 289 @typing.final 290 def acquire(self) -> RESTClient: 291 """Acquires a new `RESTClient` instance from this pool. 292 293 Returns 294 ------- 295 `RESTClient` 296 An instance of a `RESTClient`. 297 """ 298 return RESTClient( 299 self._token, 300 client_secret=self._client_secret, 301 client_id=self._client_id, 302 loads=self._loads, 303 dumps=self._dumps, 304 max_retries=self._max_retries, 305 debug=self._enable_debug, 306 client_session=self._client_session, 307 )
a Pool of RESTClient instances.
This allows to acquire multiple instances of RESTClients which can be acquired with the same token and metadata.
A full example of this client can be found in the examples directory.
Example
import aiobungie
import asyncio
pool = aiobungie.RESTPool("token")
pool.metadata['auth_code'] = 'code'
async def get() -> None:
async with pool.acquire() as rest:
this = await rest.fetch_current_user_memberships(pool.metadata['auth_code'])
await asyncio.run(get())
Parameters
- token (
str): A valid application token from Bungie's developer portal.
Other Parameters
- max_retries (
int): The max retries number to retry if the request hit a5xxstatus code. - client_secret (
str | None): An optional application client secret, This is only needed if you're fetching OAuth2 tokens with this client. - client_id (
int | None): An optional application client id, This is only needed if you're fetching OAuth2 tokens with this client. - debug (
bool | str): Whether to enable logging responses or not.
Logging Levels
False: This will disable logging.True: This will set the level toDEBUGand enable logging minimal information. Like the response status, route, taken time and so on."TRACE" | TRACE: This will log the response headers along with the minimal information.
257 def __init__( 258 self, 259 token: str, 260 /, 261 *, 262 client_secret: str | None = None, 263 client_id: int | None = None, 264 client_session: aiohttp.ClientSession | None = None, 265 dumps: typedefs.Dumps = helpers.dumps, 266 loads: typedefs.Loads = helpers.loads, 267 max_retries: int = 4, 268 debug: typing.Literal["TRACE"] | bool | int = False, 269 ) -> None: 270 self._client_secret = client_secret 271 self._client_id = client_id 272 self._token = token 273 self._max_retries = max_retries 274 self._metadata: collections.MutableMapping[typing.Any, typing.Any] = {} 275 self._enable_debug = debug 276 self._client_session = client_session 277 self._loads = loads 278 self._dumps = dumps
284 @property 285 def metadata(self) -> collections.MutableMapping[typing.Any, typing.Any]: 286 """Pool's Metadata. This is different from client instance metadata.""" 287 return self._metadata
Pool's Metadata. This is different from client instance metadata.
289 @typing.final 290 def acquire(self) -> RESTClient: 291 """Acquires a new `RESTClient` instance from this pool. 292 293 Returns 294 ------- 295 `RESTClient` 296 An instance of a `RESTClient`. 297 """ 298 return RESTClient( 299 self._token, 300 client_secret=self._client_secret, 301 client_id=self._client_id, 302 loads=self._loads, 303 dumps=self._dumps, 304 max_retries=self._max_retries, 305 debug=self._enable_debug, 306 client_session=self._client_session, 307 )
485@typing.final 486class Race(int, Enum): 487 """An Enum for Destiny races.""" 488 489 HUMAN = 0 490 AWOKEN = 1 491 EXO = 2 492 UNKNOWN = 3
An Enum for Destiny races.
132@typing.final 133class Raid(int, Enum): 134 """An Enum for all available raids in Destiny 2.""" 135 136 DSC = 910380154 137 """Deep Stone Crypt""" 138 139 LW = 2122313384 140 """Last Wish""" 141 142 VOG = 3881495763 143 """Normal Valut of Glass""" 144 145 GOS = 3458480158 146 """Garden Of Salvation"""
An Enum for all available raids in Destiny 2.
250@attrs.define(auto_exc=True) 251class RateLimitedError(HTTPError): 252 """Raised when too many request status code is returned.""" 253 254 http_status: http.HTTPStatus = attrs.field( 255 default=http.HTTPStatus.TOO_MANY_REQUESTS, init=False 256 ) 257 """The request response http status.""" 258 259 url: typedefs.StrOrURL 260 """The URL/endpoint caused this error.""" 261 262 body: typing.Any 263 """The response body.""" 264 265 retry_after: float = attrs.field(default=0.0) 266 """The amount of seconds you need to wait before retrying to requests.""" 267 268 message: str = attrs.field(init=False) 269 """A Bungie human readable message describes the cause of the error.""" 270 271 # Type Ignore: attrs provide a `.default` setter on its attribs to allow 272 # changing the default value. 273 @message.default # pyright: ignore 274 def _(self) -> str: 275 return f"You're ratelimited for {self.retry_after}, Endpoint: {self.url}. Slow down!" 276 277 def __str__(self) -> str: 278 return self.message
Raised when too many request status code is returned.
2def __init__(self, url, body, retry_after=attr_dict['retry_after'].default): 3 self.http_status = attr_dict['http_status'].default 4 self.url = url 5 self.body = body 6 self.retry_after = retry_after 7 self.message = __attr_factory_message(self) 8 BaseException.__init__(self, self.url,self.body,self.retry_after)
Method generated by attrs for class RateLimitedError.
Inherited Members
- builtins.BaseException
- with_traceback
- add_note
- args
50@typing.final 51class RecordState(enums.Flag): 52 """An enum for records component states.""" 53 54 NONE = 0 55 REDEEMED = 1 << 0 56 UNAVAILABLE = 1 << 1 57 OBJECTIVE_NOT_COMPLETED = 1 << 2 58 OBSCURED = 1 << 3 59 INVISIBLE = 1 << 4 60 ENTITLEMENT_UNOWNED = 1 << 5 61 CAN_EQUIP_TITLE = 1 << 6
An enum for records component states.
680@typing.final 681class Relationship(int, Enum): 682 """An enum for bungie friends relationship types.""" 683 684 UNKNOWN = 0 685 FRIEND = 1 686 INCOMING_REQUEST = 2 687 OUTGOING_REQUEST = 3
An enum for bungie friends relationship types.
245@attrs.define(auto_exc=True) 246class ResponseError(HTTPException): 247 """Exception for other HTTP response errors."""
Exception for other HTTP response errors.
2def __init__(self, *, error_code, http_status, throttle_seconds, url, body, headers, message, error_status, message_data): 3 self.error_code = error_code 4 self.http_status = http_status 5 self.throttle_seconds = throttle_seconds 6 self.url = url 7 self.body = body 8 self.headers = headers 9 self.message = message 10 self.error_status = error_status 11 self.message_data = message_data 12 BaseException.__init__(self, self.error_code,self.http_status,self.throttle_seconds,self.url,self.body,self.headers,self.message,self.error_status,self.message_data)
Method generated by attrs for class ResponseError.
Inherited Members
- HTTPException
- error_code
- http_status
- throttle_seconds
- url
- body
- headers
- message
- error_status
- message_data
- builtins.BaseException
- with_traceback
- add_note
- args
507@typing.final 508class Stat(int, Enum): 509 """An Enum for Destiny 2 character stats.""" 510 511 NONE = 0 512 MOBILITY = 2996146975 513 RESILIENCE = 392767087 514 RECOVERY = 1943323491 515 DISCIPLINE = 1735777505 516 INTELLECT = 144602215 517 STRENGTH = 4244567218 518 LIGHT_POWER = 1935470627
An Enum for Destiny 2 character stats.
622@typing.final 623class TierType(int, Enum): 624 """An enum for a Destiny 2 item tier type.""" 625 626 UNKNOWN = 0 627 CURRENCY = 1 628 BASIC = 2 629 COMMON = 3 630 RARE = 4 631 SUPERIOR = 5 632 EXOTIC = 6
An enum for a Destiny 2 item tier type.
732@typing.final 733class TransferStatus(Flag): 734 """An enum for items transfer statuses.""" 735 736 CAN_TRANSFER = 0 737 """The item can be transferred.""" 738 IS_EQUIPPED = 1 << 0 739 """You can't transfer since the item is equipped.""" 740 NOT_TRASNFERRABLE = 1 << 1 741 """This item can not be transferred.""" 742 COULD_BE_TRANSFERRED = 1 << 2 743 """You can transfer the item. But the place you're trying to put it at has no space for it."""
An enum for items transfer statuses.
You can transfer the item. But the place you're trying to put it at has no space for it.
74@typing.final 75class ValueUIStyle(int, enums.Enum): 76 AUTOMATIC = 0 77 FRACTION = 1 78 CHECK_BOX = 2 79 PERCENTAGE = 3 80 DATETIME = 4 81 FRACTION_FLOAT = 5 82 INTEGER = 6 83 TIME_DURATION = 7 84 HIDDEN = 8 85 MULTIPLIER = 9 86 GREEN_PIPS = 10 87 RED_PIPS = 11 88 EXPLICIT_PERCENTAGE = 12 89 RAW_FLOAT = 13 90 LEVEL_AND_REWARD = 14
int([x]) -> integer int(x, base=10) -> integer
Convert a number or string to an integer, or return 0 if no arguments are given. If x is a number, return x.__int__(). For floating point numbers, this truncates towards zero.
If x is not a number or if base is given, then x must be a string, bytes, or bytearray instance representing an integer literal in the given base. The literal can be preceded by '+' or '-' and be surrounded by whitespace. The base defaults to 10. Valid bases are 0 and 2-36. Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
229@typing.final 230class Vendor(int, Enum): 231 """An Enum for all available vendors in Destiny 2.""" 232 233 ZAVALA = 69482069 234 XUR = 2190858386 235 BANSHE = 672118013 236 SPIDER = 863940356 237 SHAXX = 3603221665 238 KADI = 529635856 239 """Postmaster exo.""" 240 YUNA = 1796504621 241 """Asia servers only.""" 242 EVERVERSE = 3361454721 243 AMANDA = 460529231 244 """Amanda holiday""" 245 CROW = 3611983588 246 HAWTHORNE = 3347378076 247 ADA1 = 350061650 248 DRIFTER = 248695599 249 IKORA = 1976548992 250 SAINT = 765357505 251 """Saint-14""" 252 ERIS_MORN = 1616085565 253 SHAW_HAWN = 1816541247 254 """COSMODROME Guy""" 255 VARIKS = 2531198101
An Enum for all available vendors in Destiny 2.
521@typing.final 522class WeaponType(int, Enum): 523 """Enums for The three Destiny Weapon Types""" 524 525 NONE = 0 526 KINETIC = 1498876634 527 ENERGY = 2465295065 528 POWER = 953998645
Enums for The three Destiny Weapon Types
614def iter( 615 iterable: collections.Iterable[Item], 616) -> Iterator[Item]: 617 """Transform an iterable into an flat iterator. 618 619 Example 620 ------- 621 ```py 622 sequence = (1,2,3) 623 for item in aiobungie.iter(sequence).reversed(): 624 print(item) 625 # 3 626 # 2 627 # 1 628 ``` 629 630 Parameters 631 ---------- 632 iterable: `typing.Iterable[Item]` 633 The iterable to convert. 634 635 Raises 636 ------ 637 `StopIteration` 638 If no elements are left in the iterator. 639 """ 640 return Iterator(iterable)
Transform an iterable into an flat iterator.
Example
sequence = (1,2,3)
for item in aiobungie.iter(sequence).reversed():
print(item)
# 3
# 2
# 1
Parameters
- iterable (
typing.Iterable[Item]): The iterable to convert.
Raises
StopIteration: If no elements are left in the iterator.
281async def panic(response: aiohttp.ClientResponse) -> HTTPError: 282 """Immediately raise an exception based on the response.""" 283 284 # Bungie get funky and return HTML instead of JSON when making an authorized 285 # request with a dummy access token. We could technically read the page content 286 # but that's Bungie's fault for not returning a JSON response. 287 if response.content_type != "application/json": 288 raise HTTPError( 289 message=f"Expected JSON response, Got {response.content_type}, " 290 f"{response.real_url.human_repr()}", 291 http_status=http.HTTPStatus(response.status), 292 ) 293 294 body: collections.Mapping[str, typing.Any] = helpers.loads(await response.read()) # type: ignore 295 message: str = body.get("Message", "UNDEFINED_MESSAGE") 296 error_status: str = body.get("ErrorStatus", "UNDEFINED_ERROR_STATUS") 297 message_data: dict[str, str] = body.get("MessageData", {}) 298 throttle_seconds: int = body.get("ThrottleSeconds", 0) 299 error_code: int = body.get("ErrorCode", 0) 300 301 # Standard HTTP status. 302 match response.status: 303 case http.HTTPStatus.NOT_FOUND: 304 return NotFound( 305 message=message, 306 error_code=error_code, 307 throttle_seconds=throttle_seconds, 308 url=str(response.real_url), 309 body=body, 310 headers=response.headers, 311 error_status=error_status, 312 message_data=message_data, 313 ) 314 315 case http.HTTPStatus.FORBIDDEN: 316 return Forbidden( 317 message=message, 318 error_code=error_code, 319 throttle_seconds=throttle_seconds, 320 url=str(response.real_url), 321 body=body, 322 headers=response.headers, 323 error_status=error_status, 324 message_data=message_data, 325 ) 326 327 case http.HTTPStatus.UNAUTHORIZED: 328 return Unauthorized( 329 message=message, 330 error_code=error_code, 331 throttle_seconds=throttle_seconds, 332 url=str(response.real_url), 333 body=body, 334 headers=response.headers, 335 error_status=error_status, 336 message_data=message_data, 337 ) 338 339 case http.HTTPStatus.BAD_REQUEST: 340 # Membership needs to be alone. 341 if error_status == "InvalidParameters": 342 return MembershipTypeError( 343 message=message, 344 body=body, 345 headers=response.headers, 346 url=str(response.url), 347 membership_type=message_data["membershipType"], 348 required_membership=message_data["membershipInfo.membershipType"], 349 membership_id=int(message_data["membershipId"]), 350 ) 351 return BadRequest( 352 message=message, 353 body=body, 354 headers=response.headers, 355 url=str(response.url), 356 ) 357 case _: 358 status = http.HTTPStatus(response.status) 359 360 if 400 <= status < 500: 361 return ResponseError( 362 message=message, 363 error_code=error_code, 364 throttle_seconds=throttle_seconds, 365 url=str(response.real_url), 366 body=body, 367 headers=response.headers, 368 error_status=error_status, 369 message_data=message_data, 370 http_status=status, 371 ) 372 373 # Need to self handle ~5xx errors 374 elif 500 <= status < 600: 375 # No API key or method requires OAuth2 most likely. 376 if error_status in { 377 "ApiKeyMissingFromRequest", 378 "WebAuthRequired", 379 "ApiInvalidOrExpiredKey", 380 "AuthenticationInvalid", 381 "AuthorizationCodeInvalid", 382 }: 383 return Unauthorized( 384 message=message, 385 error_code=error_code, 386 throttle_seconds=throttle_seconds, 387 url=str(response.real_url), 388 body=body, 389 headers=response.headers, 390 error_status=error_status, 391 message_data=message_data, 392 ) 393 394 # Anything contains not found. 395 elif ( 396 "NotFound" in error_status 397 or error_status == "UserCannotFindRequestedUser" 398 ): 399 return NotFound( 400 message=message, 401 error_code=error_code, 402 throttle_seconds=throttle_seconds, 403 url=str(response.real_url), 404 body=body, 405 headers=response.headers, 406 error_status=error_status, 407 message_data=message_data, 408 ) 409 410 # Other 5xx errors. 411 else: 412 return InternalServerError( 413 message=message, 414 error_code=error_code, 415 throttle_seconds=throttle_seconds, 416 url=str(response.real_url), 417 body=body, 418 headers=response.headers, 419 error_status=error_status, 420 message_data=message_data, 421 http_status=status, 422 ) 423 # Something else. 424 else: 425 return HTTPException( 426 message=message, 427 error_code=error_code, 428 throttle_seconds=throttle_seconds, 429 url=str(response.real_url), 430 body=body, 431 headers=response.headers, 432 error_status=error_status, 433 message_data=message_data, 434 http_status=status, 435 )
Immediately raise an exception based on the response.
438def stringify_headers(headers: collections.Mapping[str, typing.Any]) -> str: 439 return ( 440 "{ \n" 441 + "\n".join( # noqa: W503 442 f"{f' {key}'}: {value}" 443 if key 444 not in { 445 "Authorization", 446 "X-API-KEY", 447 "client_secret", 448 "client_id", 449 "access_token", 450 "refresh_token", 451 } 452 else f" {key}: REDACTED_TOKEN" 453 for key, value in headers.items() 454 ) 455 + "\n}" # noqa: W503 456 )